tint/ast: Migrate to utils::Vector

Change-Id: I10dd2feeaeb86a1ee7769d2bfd172e49c2805cb3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97843
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/resolver/array_accessor_test.cc b/src/tint/resolver/array_accessor_test.cc
index 0f72cf8..a07f6df 100644
--- a/src/tint/resolver/array_accessor_test.cc
+++ b/src/tint/resolver/array_accessor_test.cc
@@ -282,8 +282,8 @@
     auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
     auto* acc = IndexAccessor("a", Expr(Source{{12, 34}}, idx));
     auto* f = Var("f", ty.f32(), acc);
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(a),
              Decl(idx),
              Decl(f),
@@ -303,8 +303,8 @@
     // var f : f32 = a[2.0f];
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
     auto* f = Var("a_2", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, 2_f)));
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(a),
              Decl(f),
          });
@@ -318,8 +318,8 @@
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
     auto* acc = IndexAccessor("a", 2_i);
     auto* f = Var("a_2", ty.f32(), acc);
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(a),
              Decl(f),
          });
@@ -342,7 +342,7 @@
     auto* star_p = Deref(p);
     auto* acc = IndexAccessor(Source{{12, 34}}, star_p, idx);
     auto* x = Var("x", ty.f32(), acc);
-    Func("func", {p}, ty.f32(), {Decl(idx), Decl(x), Return(x)});
+    Func("func", utils::Vector{p}, ty.f32(), utils::Vector{Decl(idx), Decl(x), Return(x)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -363,7 +363,7 @@
     auto* accessor_expr = IndexAccessor(Source{{12, 34}}, p, idx);
     auto* star_p = Deref(accessor_expr);
     auto* x = Var("x", ty.f32(), star_p);
-    Func("func", {p}, ty.f32(), {Decl(idx), Decl(x), Return(x)});
+    Func("func", utils::Vector{p}, ty.f32(), utils::Vector{Decl(idx), Decl(x), Return(x)});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/assignment_validation_test.cc b/src/tint/resolver/assignment_validation_test.cc
index e204a62..d6a7e5c 100644
--- a/src/tint/resolver/assignment_validation_test.cc
+++ b/src/tint/resolver/assignment_validation_test.cc
@@ -29,9 +29,11 @@
     // struct S { m : i32 };
     // @group(0) @binding(0)
     // var<storage,read> a : S;
-    auto* s = Structure("S", {Member("m", ty.i32())});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("m", ty.i32()),
+                             });
     GlobalVar(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -236,12 +238,12 @@
     };
 
     GlobalVar("a", make_type(), ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
     GlobalVar("b", make_type(), ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -257,9 +259,11 @@
     // @group(0) @binding(0) var<storage, read_write> v : S;
     // v.a = v.a;
 
-    auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("a", ty.atomic(ty.i32())),
+                             });
     GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -275,9 +279,11 @@
     // @group(0) @binding(0) var<storage, read_write> v : S;
     // v.a = v.a;
 
-    auto* s = Structure("S", {Member("a", ty.array(ty.f32()))});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("a", ty.array(ty.f32())),
+                             });
     GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -296,7 +302,9 @@
     // fn f() {
     //   _ = s;
     // }
-    auto* s = Structure("S", {Member("arr", ty.array<i32>())});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("arr", ty.array<i32>()),
+                             });
     GlobalVar("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
 
     WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
@@ -316,7 +324,9 @@
     // fn f() {
     //   _ = s.arr;
     // }
-    auto* s = Structure("S", {Member("arr", ty.array<i32>())});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("arr", ty.array<i32>()),
+                             });
     GlobalVar("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
 
     WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
@@ -360,11 +370,13 @@
     //   _ = wg;
     //   _ = wg[3i];
     // }
-    auto* S = Structure("S", {
+    auto* S = Structure("S", utils::Vector{
                                  Member("i", ty.i32()),
                                  Member("arr", ty.array<i32>()),
                              });
-    auto* U = Structure("U", {Member("i", ty.i32())});
+    auto* U = Structure("U", utils::Vector{
+                                 Member("i", ty.i32()),
+                             });
     GlobalVar("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
               GroupAndBinding(0, 0));
     GlobalVar("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
diff --git a/src/tint/resolver/atomics_test.cc b/src/tint/resolver/atomics_test.cc
index 09ca0ba..d7b3824 100644
--- a/src/tint/resolver/atomics_test.cc
+++ b/src/tint/resolver/atomics_test.cc
@@ -45,9 +45,9 @@
 }
 
 TEST_F(ResolverAtomicTest, GlobalStorageStruct) {
-    auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
     auto* g = GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                        ast::AttributeList{
+                        utils::Vector{
                             create<ast::BindingAttribute>(0u),
                             create<ast::GroupAttribute>(0u),
                         });
diff --git a/src/tint/resolver/atomics_validation_test.cc b/src/tint/resolver/atomics_validation_test.cc
index 5ec4e01..0961397 100644
--- a/src/tint/resolver/atomics_validation_test.cc
+++ b/src/tint/resolver/atomics_validation_test.cc
@@ -40,7 +40,7 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, StorageClass_Storage_Struct) {
-    auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
     GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
               GroupAndBinding(0, 0));
 
@@ -73,7 +73,7 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Struct) {
-    auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
     GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -88,8 +88,9 @@
     // struct Outer { m : array<Inner, 4>; };
     // var<private> g : Outer;
 
-    auto* Inner = Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+    auto* Inner =
+        Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
     GlobalVar("g", ty.Of(Outer), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -104,8 +105,9 @@
     // struct Outer { m : array<Inner, 4>; };
     // var<private> g : Outer;
 
-    auto* Inner = Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
-    auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+    auto* Inner =
+        Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
+    auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
     GlobalVar("g", ty.Of(Outer), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -135,7 +137,7 @@
     // };
     // var<private> v: array<S, 5u>;
 
-    auto* s = Structure("S", {Member("m", ty.atomic<u32>())});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.atomic<u32>())});
     GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -154,7 +156,7 @@
 
     auto* atomic_array =
         Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
-    auto* s = Structure("S", {Member("m", ty.Of(atomic_array))});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.Of(atomic_array))});
     GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -185,17 +187,17 @@
     auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
     auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
 
-    auto* s6 = Structure("S6", {Member("x", array_i32_4)});
-    auto* s5 = Structure("S5", {Member("x", ty.Of(s6)),             //
-                                Member("y", ty.Of(atomic_array)),   //
-                                Member("z", array_atomic_u32_8)});  //
-    auto* s4 = Structure("S4", {Member("x", ty.Of(s6)),             //
-                                Member("y", ty.Of(s5)),             //
-                                Member("z", array_atomic_i32_4)});  //
-    auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
-    auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
-    auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
-    auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
+    auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
+    auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)),             //
+                                             Member("y", ty.Of(atomic_array)),   //
+                                             Member("z", array_atomic_u32_8)});  //
+    auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)),             //
+                                             Member("y", ty.Of(s5)),             //
+                                             Member("z", array_atomic_i32_4)});  //
+    auto* s3 = Structure("S3", utils::Vector{Member("x", ty.Of(s4))});
+    auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
+    auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
+    auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -206,7 +208,7 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
-    auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
               GroupAndBinding(0, 0));
 
@@ -218,7 +220,7 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
-    auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
               GroupAndBinding(0, 0));
 
@@ -234,8 +236,9 @@
     // struct Outer { m : array<Inner, 4>; };
     // var<storage, read> g : Outer;
 
-    auto* Inner = Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+    auto* Inner =
+        Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
+    auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
               GroupAndBinding(0, 0));
 
@@ -251,8 +254,9 @@
     // struct Outer { m : array<Inner, 4>; };
     // var<storage, read> g : Outer;
 
-    auto* Inner = Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
-    auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+    auto* Inner =
+        Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
+    auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
               GroupAndBinding(0, 0));
 
@@ -284,17 +288,17 @@
     auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
     auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
 
-    auto* s6 = Structure("S6", {Member("x", array_i32_4)});
-    auto* s5 = Structure("S5", {Member("x", ty.Of(s6)),             //
-                                Member("y", ty.Of(atomic_array)),   //
-                                Member("z", array_atomic_u32_8)});  //
-    auto* s4 = Structure("S4", {Member("x", ty.Of(s6)),             //
-                                Member("y", ty.Of(s5)),             //
-                                Member("z", array_atomic_i32_4)});  //
-    auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
-    auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
-    auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
-    auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
+    auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
+    auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)),             //
+                                             Member("y", ty.Of(atomic_array)),   //
+                                             Member("z", array_atomic_u32_8)});  //
+    auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)),             //
+                                             Member("y", ty.Of(s5)),             //
+                                             Member("z", array_atomic_i32_4)});  //
+    auto* s3 = Structure("S3", utils::Vector{Member("x", ty.Of(s4))});
+    auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
+    auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
+    auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage, ast::Access::kRead,
               GroupAndBinding(0, 0));
 
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 36221e2..ed0227e 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -84,9 +84,9 @@
 };
 struct TestWithParams : ResolverTestWithParam<TestParams> {};
 
-static ast::AttributeList createAttributes(const Source& source,
-                                           ProgramBuilder& builder,
-                                           AttributeKind kind) {
+static utils::Vector<const ast::Attribute*, 2> createAttributes(const Source& source,
+                                                                ProgramBuilder& builder,
+                                                                AttributeKind kind) {
     switch (kind) {
         case AttributeKind::kAlign:
             return {builder.create<ast::StructMemberAlignAttribute>(source, 4u)};
@@ -128,10 +128,10 @@
     auto& params = GetParam();
 
     Func("main",
-         {
+         utils::Vector{
              Param("a", ty.vec4<f32>(), createAttributes({}, *this, params.kind)),
          },
-         ty.void_(), {});
+         ty.void_(), utils::Empty);
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -163,11 +163,11 @@
 TEST_P(FunctionReturnTypeAttributeTest, IsValid) {
     auto& params = GetParam();
 
-    Func("main", {}, ty.f32(),
-         {
+    Func("main", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(1_f),
          },
-         {}, createAttributes({}, *this, params.kind));
+         utils::Empty, createAttributes({}, *this, params.kind));
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -201,11 +201,11 @@
 TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
     auto& params = GetParam();
     Func("main",
-         {
+         utils::Vector{
              Param("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind)),
          },
-         ty.void_(), {},
-         {
+         ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(1_i),
          });
@@ -250,11 +250,11 @@
     auto& params = GetParam();
     auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
     if (params.kind != AttributeKind::kBuiltin && params.kind != AttributeKind::kLocation) {
-        attrs.push_back(Builtin(Source{{34, 56}}, ast::BuiltinValue::kPosition));
+        attrs.Push(Builtin(Source{{34, 56}}, ast::BuiltinValue::kPosition));
     }
     auto* p = Param("a", ty.vec4<f32>(), attrs);
-    Func("frag_main", {p}, ty.void_(), {},
-         {
+    Func("frag_main", utils::Vector{p}, ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -287,17 +287,17 @@
     auto& params = GetParam();
     auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
     if (params.kind != AttributeKind::kLocation) {
-        attrs.push_back(Location(Source{{34, 56}}, 2));
+        attrs.Push(Location(Source{{34, 56}}, 2));
     }
     auto* p = Param("a", ty.vec4<f32>(), attrs);
-    Func("vertex_main", {p}, ty.vec4<f32>(),
-         {
+    Func("vertex_main", utils::Vector{p}, ty.vec4<f32>(),
+         utils::Vector{
              Return(Construct(ty.vec4<f32>())),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          },
-         {
+         utils::Vector{
              Builtin(ast::BuiltinValue::kPosition),
          });
 
@@ -338,11 +338,11 @@
 using ComputeShaderReturnTypeAttributeTest = TestWithParams;
 TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
     auto& params = GetParam();
-    Func("main", {}, ty.vec4<f32>(),
-         {
+    Func("main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
              Return(Construct(ty.vec4<f32>(), 1_f)),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(1_i),
          },
@@ -389,9 +389,10 @@
 TEST_P(FragmentShaderReturnTypeAttributeTest, IsValid) {
     auto& params = GetParam();
     auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
-    attrs.push_back(Location(Source{{34, 56}}, 2));
-    Func("frag_main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {
+    attrs.Push(Location(Source{{34, 56}}, 2));
+    Func("frag_main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{Return(Construct(ty.vec4<f32>()))},
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
          attrs);
@@ -442,13 +443,13 @@
     auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
     // a vertex shader must include the 'position' builtin in its return type
     if (params.kind != AttributeKind::kBuiltin) {
-        attrs.push_back(Builtin(Source{{34, 56}}, ast::BuiltinValue::kPosition));
+        attrs.Push(Builtin(Source{{34, 56}}, ast::BuiltinValue::kPosition));
     }
-    Func("vertex_main", {}, ty.vec4<f32>(),
-         {
+    Func("vertex_main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
              Return(Construct(ty.vec4<f32>())),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          },
          attrs);
@@ -487,14 +488,14 @@
 
 using EntryPointParameterAttributeTest = TestWithParams;
 TEST_F(EntryPointParameterAttributeTest, DuplicateAttribute) {
-    Func("main", {}, ty.f32(),
-         {
+    Func("main", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(Source{{12, 34}}, 2),
              Location(Source{{56, 78}}, 3),
          });
@@ -507,14 +508,14 @@
 
 TEST_F(EntryPointParameterAttributeTest, DuplicateInternalAttribute) {
     auto* s = Param("s", ty.sampler(ast::SamplerKind::kSampler),
-                    ast::AttributeList{
+                    utils::Vector{
                         create<ast::BindingAttribute>(0u),
                         create<ast::GroupAttribute>(0u),
                         Disable(ast::DisabledValidation::kBindingPointCollision),
                         Disable(ast::DisabledValidation::kEntryPointParameter),
                     });
-    Func("f", {s}, ty.void_(), {},
-         {
+    Func("f", utils::Vector{s}, ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -523,14 +524,14 @@
 
 using EntryPointReturnTypeAttributeTest = ResolverTest;
 TEST_F(EntryPointReturnTypeAttributeTest, DuplicateAttribute) {
-    Func("main", {}, ty.f32(),
-         {
+    Func("main", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         ast::AttributeList{
+         utils::Vector{
              Location(Source{{12, 34}}, 2),
              Location(Source{{56, 78}}, 3),
          });
@@ -542,11 +543,11 @@
 }
 
 TEST_F(EntryPointReturnTypeAttributeTest, DuplicateInternalAttribute) {
-    Func("f", {}, ty.i32(), {Return(1_i)},
-         {
+    Func("f", utils::Empty, ty.i32(), utils::Vector{Return(1_i)},
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         ast::AttributeList{
+         utils::Vector{
              Disable(ast::DisabledValidation::kBindingPointCollision),
              Disable(ast::DisabledValidation::kEntryPointParameter),
          });
@@ -561,7 +562,7 @@
 TEST_P(StructAttributeTest, IsValid) {
     auto& params = GetParam();
 
-    auto* str = create<ast::Struct>(Sym("mystruct"), ast::StructMemberList{Member("a", ty.f32())},
+    auto* str = create<ast::Struct>(Sym("mystruct"), utils::Vector{Member("a", ty.f32())},
                                     createAttributes(Source{{12, 34}}, *this, params.kind));
     AST().AddGlobalDeclaration(str);
 
@@ -592,16 +593,14 @@
 using StructMemberAttributeTest = TestWithParams;
 TEST_P(StructMemberAttributeTest, IsValid) {
     auto& params = GetParam();
-    ast::StructMemberList members;
+    utils::Vector<const ast::StructMember*, 1> members;
     if (params.kind == AttributeKind::kBuiltin) {
-        members.push_back(
-            {Member("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind))});
+        members.Push(
+            Member("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind)));
     } else {
-        members.push_back(
-            {Member("a", ty.f32(), createAttributes(Source{{12, 34}}, *this, params.kind))});
+        members.Push(Member("a", ty.f32(), createAttributes(Source{{12, 34}}, *this, params.kind)));
     }
     Structure("mystruct", members);
-    WrapInFunction();
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
     } else {
@@ -627,38 +626,35 @@
                                          TestParams{AttributeKind::kBindingAndGroup, false}));
 TEST_F(StructMemberAttributeTest, DuplicateAttribute) {
     Structure("mystruct",
-              {
+              utils::Vector{
                   Member("a", ty.i32(),
-                         {
+                         utils::Vector{
                              create<ast::StructMemberAlignAttribute>(Source{{12, 34}}, 4u),
                              create<ast::StructMemberAlignAttribute>(Source{{56, 78}}, 8u),
                          }),
               });
-    WrapInFunction();
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(56:78 error: duplicate align attribute
 12:34 note: first attribute declared here)");
 }
 TEST_F(StructMemberAttributeTest, InvariantAttributeWithPosition) {
-    Structure("mystruct", {
+    Structure("mystruct", utils::Vector{
                               Member("a", ty.vec4<f32>(),
-                                     {
+                                     utils::Vector{
                                          Invariant(),
                                          Builtin(ast::BuiltinValue::kPosition),
                                      }),
                           });
-    WrapInFunction();
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 TEST_F(StructMemberAttributeTest, InvariantAttributeWithoutPosition) {
-    Structure("mystruct", {
+    Structure("mystruct", utils::Vector{
                               Member("a", ty.vec4<f32>(),
-                                     {
+                                     utils::Vector{
                                          Invariant(Source{{12, 34}}),
                                      }),
                           });
-    WrapInFunction();
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: invariant attribute must only be applied to a "
@@ -672,12 +668,10 @@
     auto& params = GetParam();
 
     auto* arr = ty.array(ty.f32(), nullptr, createAttributes(Source{{12, 34}}, *this, params.kind));
-    Structure("mystruct", {
+    Structure("mystruct", utils::Vector{
                               Member("a", arr),
                           });
 
-    WrapInFunction();
-
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
     } else {
@@ -714,8 +708,6 @@
                   createAttributes(Source{{12, 34}}, *this, params.kind));
     }
 
-    WrapInFunction();
-
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
     } else {
@@ -744,14 +736,12 @@
 
 TEST_F(VariableAttributeTest, DuplicateAttribute) {
     GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler),
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(Source{{12, 34}}, 2u),
                   create<ast::GroupAttribute>(2u),
                   create<ast::BindingAttribute>(Source{{56, 78}}, 3u),
               });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(56:78 error: duplicate binding attribute
@@ -760,7 +750,7 @@
 
 TEST_F(VariableAttributeTest, LocalVariable) {
     auto* v = Var("a", ty.f32(),
-                  ast::AttributeList{
+                  utils::Vector{
                       create<ast::BindingAttribute>(Source{{12, 34}}, 2u),
                   });
 
@@ -777,8 +767,6 @@
     GlobalConst("a", ty.f32(), Expr(1.23_f),
                 createAttributes(Source{{12, 34}}, *this, params.kind));
 
-    WrapInFunction();
-
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
     } else {
@@ -806,13 +794,11 @@
 
 TEST_F(ConstantAttributeTest, DuplicateAttribute) {
     GlobalConst("a", ty.f32(), Expr(1.23_f),
-                ast::AttributeList{
+                utils::Vector{
                     create<ast::IdAttribute>(Source{{12, 34}}, 0u),
                     create<ast::IdAttribute>(Source{{56, 78}}, 1u),
                 });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(56:78 error: duplicate id attribute
@@ -825,8 +811,6 @@
 
     Override("a", ty.f32(), Expr(1.23_f), createAttributes(Source{{12, 34}}, *this, params.kind));
 
-    WrapInFunction();
-
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
     } else {
@@ -853,13 +837,11 @@
 
 TEST_F(OverrideAttributeTest, DuplicateAttribute) {
     Override("a", ty.f32(), Expr(1.23_f),
-             ast::AttributeList{
+             utils::Vector{
                  create<ast::IdAttribute>(Source{{12, 34}}, 0u),
                  create<ast::IdAttribute>(Source{{56, 78}}, 1u),
              });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(56:78 error: duplicate id attribute
@@ -895,8 +877,10 @@
        << ", should_pass: " << params.should_pass;
     SCOPED_TRACE(ss.str());
 
-    auto* arr =
-        ty.array(el_ty, 4_u, {create<ast::StrideAttribute>(Source{{12, 34}}, params.stride)});
+    auto* arr = ty.array(el_ty, 4_u,
+                         utils::Vector{
+                             create<ast::StrideAttribute>(Source{{12, 34}}, params.stride),
+                         });
 
     GlobalVar("myarray", arr, ast::StorageClass::kPrivate);
 
@@ -976,7 +960,7 @@
 
 TEST_F(ArrayStrideTest, DuplicateAttribute) {
     auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4_u,
-                         {
+                         utils::Vector{
                              create<ast::StrideAttribute>(Source{{12, 34}}, 4u),
                              create<ast::StrideAttribute>(Source{{56, 78}}, 4u),
                          });
@@ -997,7 +981,9 @@
 
 using ResourceAttributeTest = ResolverTest;
 TEST_F(ResourceAttributeTest, UniformBufferMissingBinding) {
-    auto* s = Structure("S", {Member("x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("x", ty.i32()),
+                             });
     GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
 
     EXPECT_FALSE(r()->Resolve());
@@ -1006,7 +992,9 @@
 }
 
 TEST_F(ResourceAttributeTest, StorageBufferMissingBinding) {
-    auto* s = Structure("S", {Member("x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("x", ty.i32()),
+                             });
     GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead);
 
     EXPECT_FALSE(r()->Resolve());
@@ -1035,7 +1023,7 @@
 TEST_F(ResourceAttributeTest, BindingPairMissingBinding) {
     GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
               ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::GroupAttribute>(1u),
               });
 
@@ -1047,7 +1035,7 @@
 TEST_F(ResourceAttributeTest, BindingPairMissingGroup) {
     GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
               ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
               });
 
@@ -1059,25 +1047,25 @@
 TEST_F(ResourceAttributeTest, BindingPointUsedTwiceByEntryPoint) {
     GlobalVar(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
               ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(2u),
               });
     GlobalVar(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
               ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(2u),
               });
 
-    Func("F", {}, ty.void_(),
-         {
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "A", vec2<i32>(1_i, 2_i), 0_i))),
              Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "B", vec2<i32>(1_i, 2_i), 0_i))),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1091,31 +1079,31 @@
 TEST_F(ResourceAttributeTest, BindingPointUsedTwiceByDifferentEntryPoints) {
     GlobalVar(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
               ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(2u),
               });
     GlobalVar(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
               ast::StorageClass::kNone,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(2u),
               });
 
-    Func("F_A", {}, ty.void_(),
-         {
+    Func("F_A", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "A", vec2<i32>(1_i, 2_i), 0_i))),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
-    Func("F_B", {}, ty.void_(),
-         {
+    Func("F_B", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
                       Call("textureLoad", "B", vec2<i32>(1_i, 2_i), 0_i))),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1124,7 +1112,7 @@
 
 TEST_F(ResourceAttributeTest, BindingPointOnNonResource) {
     GlobalVar(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(2u),
               });
@@ -1141,32 +1129,38 @@
 namespace {
 using InvariantAttributeTests = ResolverTest;
 TEST_F(InvariantAttributeTests, InvariantWithPosition) {
-    auto* param = Param(
-        "p", ty.vec4<f32>(),
-        {Invariant(Source{{12, 34}}), Builtin(Source{{56, 78}}, ast::BuiltinValue::kPosition)});
-    Func("main", {param}, ty.vec4<f32>(),
-         {
+    auto* param = Param("p", ty.vec4<f32>(),
+                        utils::Vector{
+                            Invariant(Source{{12, 34}}),
+                            Builtin(Source{{56, 78}}, ast::BuiltinValue::kPosition),
+                        });
+    Func("main", utils::Vector{param}, ty.vec4<f32>(),
+         utils::Vector{
              Return(Construct(ty.vec4<f32>())),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(InvariantAttributeTests, InvariantWithoutPosition) {
-    auto* param = Param("p", ty.vec4<f32>(), {Invariant(Source{{12, 34}}), Location(0)});
-    Func("main", {param}, ty.vec4<f32>(),
-         {
+    auto* param = Param("p", ty.vec4<f32>(),
+                        utils::Vector{
+                            Invariant(Source{{12, 34}}),
+                            Location(0),
+                        });
+    Func("main", utils::Vector{param}, ty.vec4<f32>(),
+         utils::Vector{
              Return(Construct(ty.vec4<f32>())),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -1182,15 +1176,20 @@
 
 using WorkgroupAttribute = ResolverTest;
 TEST_F(WorkgroupAttribute, ComputeShaderPass) {
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(WorkgroupAttribute, Missing) {
-    Func(Source{{12, 34}}, "main", {}, ty.void_(), {}, {Stage(ast::PipelineStage::kCompute)});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1199,8 +1198,10 @@
 }
 
 TEST_F(WorkgroupAttribute, NotAnEntryPoint) {
-    Func("main", {}, ty.void_(), {},
-         {create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1209,9 +1210,11 @@
 }
 
 TEST_F(WorkgroupAttribute, NotAComputeShader) {
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment),
-          create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1220,8 +1223,8 @@
 }
 
 TEST_F(WorkgroupAttribute, DuplicateAttribute) {
-    Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
-         {
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(Source{{12, 34}}, 1_i, nullptr, nullptr),
              WorkgroupSize(Source{{56, 78}}, 2_i, nullptr, nullptr),
@@ -1254,15 +1257,15 @@
     auto& params = GetParam();
 
     Func("main",
-         {
+         utils::Vector{
              Param("a", ty.f32(),
-                   {
+                   utils::Vector{
                        Location(0),
                        Interpolate(Source{{12, 34}}, params.type, params.sampling),
                    }),
          },
-         ty.void_(), {},
-         {
+         ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1280,15 +1283,15 @@
     auto& params = GetParam();
 
     Func("main",
-         {
+         utils::Vector{
              Param("a", ty.i32(),
-                   {
+                   utils::Vector{
                        Location(0),
                        Interpolate(Source{{12, 34}}, params.type, params.sampling),
                    }),
          },
-         ty.void_(), {},
-         {
+         ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1311,15 +1314,15 @@
     auto& params = GetParam();
 
     Func("main",
-         {
+         utils::Vector{
              Param("a", ty.vec4<u32>(),
-                   {
+                   utils::Vector{
                        Location(0),
                        Interpolate(Source{{12, 34}}, params.type, params.sampling),
                    }),
          },
-         ty.void_(), {},
-         {
+         ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1357,8 +1360,9 @@
         Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kSample, false}));
 
 TEST_F(InterpolateTest, FragmentInput_Integer_MissingFlatInterpolation) {
-    Func("main", {Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)})}, ty.void_(), {},
-         {
+    Func("main", utils::Vector{Param(Source{{12, 34}}, "a", ty.i32(), utils::Vector{Location(0)})},
+         ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1369,16 +1373,17 @@
 }
 
 TEST_F(InterpolateTest, VertexOutput_Integer_MissingFlatInterpolation) {
-    auto* s =
-        Structure("S", {
-                           Member("pos", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)}),
-                           Member(Source{{12, 34}}, "u", ty.u32(), {Location(0)}),
-                       });
-    Func("main", {}, ty.Of(s),
-         {
+    auto* s = Structure(
+        "S",
+        utils::Vector{
+            Member("pos", ty.vec4<f32>(), utils::Vector{Builtin(ast::BuiltinValue::kPosition)}),
+            Member(Source{{12, 34}}, "u", ty.u32(), utils::Vector{Location(0)}),
+        });
+    Func("main", utils::Empty, ty.Of(s),
+         utils::Vector{
              Return(Construct(ty.Of(s))),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          });
 
@@ -1391,16 +1396,16 @@
 
 TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
     Func("main",
-         {
+         utils::Vector{
              Param("a", ty.vec4<f32>(),
-                   {
+                   utils::Vector{
                        Builtin(ast::BuiltinValue::kPosition),
                        Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
                                    ast::InterpolationSampling::kNone),
                    }),
          },
-         ty.void_(), {},
-         {
+         ty.void_(), utils::Empty,
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -1410,14 +1415,14 @@
 }
 
 TEST_F(InterpolateTest, MissingLocationAttribute_ReturnType) {
-    Func("main", {}, ty.vec4<f32>(),
-         {
+    Func("main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
              Return(Construct(ty.vec4<f32>())),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          },
-         {
+         utils::Vector{
              Builtin(ast::BuiltinValue::kPosition),
              Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
                          ast::InterpolationSampling::kNone),
@@ -1429,9 +1434,12 @@
 }
 
 TEST_F(InterpolateTest, MissingLocationAttribute_Struct) {
-    Structure("S", {Member("a", ty.f32(),
-                           {Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
-                                        ast::InterpolationSampling::kNone)})});
+    Structure("S",
+              utils::Vector{
+                  Member("a", ty.f32(),
+                         utils::Vector{Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+                                                   ast::InterpolationSampling::kNone)}),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index fe16221..3bc6ecd 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -44,6 +44,8 @@
 namespace tint::resolver {
 namespace {
 
+using ExpressionList = utils::Vector<const ast::Expression*, 8>;
+
 using BuiltinType = sem::BuiltinType;
 
 using ResolverBuiltinTest = ResolverTest;
@@ -209,9 +211,9 @@
 
 TEST_F(ResolverBuiltinArrayTest, ArrayLength_Vector) {
     auto* ary = ty.array<i32>();
-    auto* str = Structure("S", {Member("x", ary)});
+    auto* str = Structure("S", utils::Vector{Member("x", ary)});
     GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -1906,8 +1908,8 @@
     GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* expr = Call(name, "ident");
-    Func("func", {}, ty.void_(), {Ignore(expr)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
+         utils::Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1920,8 +1922,8 @@
     GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call(name, "ident");
-    Func("func", {}, ty.void_(), {Ignore(expr)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
+         utils::Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -2008,10 +2010,10 @@
         return nullptr;
     }
 
-    void add_call_param(std::string name, const ast::Type* type, ast::ExpressionList* call_params) {
+    void add_call_param(std::string name, const ast::Type* type, ExpressionList* call_params) {
         if (type->IsAnyOf<ast::Texture, ast::Sampler>()) {
             GlobalVar(name, type,
-                      ast::AttributeList{
+                      utils::Vector{
                           create<ast::BindingAttribute>(0u),
                           create<ast::GroupAttribute>(0u),
                       });
@@ -2020,7 +2022,7 @@
             GlobalVar(name, type, ast::StorageClass::kPrivate);
         }
 
-        call_params->push_back(Expr(name));
+        call_params->Push(Expr(name));
     }
     const ast::Type* subtype(Texture type) {
         if (type == Texture::kF32) {
@@ -2042,7 +2044,7 @@
     auto* coords_type = GetCoordsType(dim, ty.i32());
     auto* texture_type = ty.sampled_texture(dim, s);
 
-    ast::ExpressionList call_params;
+    ExpressionList call_params;
 
     add_call_param("texture", texture_type, &call_params);
     add_call_param("coords", coords_type, &call_params);
@@ -2337,7 +2339,8 @@
 
     auto* call = Call(param.function, param.args(this));
     auto* stmt = CallStmt(call);
-    Func("func", {}, ty.void_(), {stmt}, {Stage(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{stmt},
+         utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index be7a23f..83603c3 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -24,8 +24,8 @@
 
 TEST_F(ResolverBuiltinValidationTest, FunctionTypeMustMatchReturnStatementType_void_fail) {
     // fn func { return workgroupBarrier(); }
-    Func("func", {}, ty.void_(),
-         {
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
              Return(Call(Source{Source::Location{12, 34}}, "workgroupBarrier")),
          });
 
@@ -36,13 +36,15 @@
 TEST_F(ResolverBuiltinValidationTest, InvalidPipelineStageDirect) {
     // @compute @workgroup_size(1) fn func { return dpdx(1.0); }
 
-    auto* dpdx =
-        create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1_f)});
-    Func(Source{{1, 2}}, "func", {}, ty.void_(),
-         {
+    auto* dpdx = create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"),
+                                             utils::Vector{
+                                                 Expr(1_f),
+                                             });
+    Func(Source{{1, 2}}, "func", utils::Empty, ty.void_(),
+         utils::Vector{
              CallStmt(dpdx),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kCompute),
              WorkgroupSize(1_i),
          });
@@ -57,16 +59,33 @@
     // fn f2 { f1(); }
     // @compute @workgroup_size(1) fn main { return f2(); }
 
-    auto* dpdx =
-        create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1_f)});
-    Func(Source{{1, 2}}, "f0", {}, ty.void_(), {CallStmt(dpdx)});
+    auto* dpdx = create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"),
+                                             utils::Vector{
+                                                 Expr(1_f),
+                                             });
+    Func(Source{{1, 2}}, "f0", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(dpdx),
+         });
 
-    Func(Source{{3, 4}}, "f1", {}, ty.void_(), {CallStmt(Call("f0"))});
+    Func(Source{{3, 4}}, "f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f0")),
+         });
 
-    Func(Source{{5, 6}}, "f2", {}, ty.void_(), {CallStmt(Call("f1"))});
+    Func(Source{{5, 6}}, "f2", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f1")),
+         });
 
-    Func(Source{{7, 8}}, "main", {}, ty.void_(), {CallStmt(Call("f2"))},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    Func(Source{{7, 8}}, "main", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f2")),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -78,7 +97,7 @@
 }
 
 TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunction) {
-    Func(Source{{12, 34}}, "mix", {}, ty.i32(), {});
+    Func(Source{{12, 34}}, "mix", utils::Empty, ty.i32(), {});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -111,7 +130,10 @@
 }
 
 TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStruct) {
-    Structure(Source{{12, 34}}, "mix", {Member("m", ty.i32())});
+    Structure(Source{{12, 34}}, "mix",
+              utils::Vector{
+                  Member("m", ty.i32()),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -232,7 +254,7 @@
     overload.BuildSamplerVariable(this);
 
     auto args = overload.args(this);
-    auto*& arg_to_replace = (param.position == Position::kFirst) ? args.front() : args.back();
+    auto*& arg_to_replace = (param.position == Position::kFirst) ? args.Front() : args.Back();
 
     // BuildTextureVariable() uses a Literal for scalars, and a CallExpression for
     // a vector constructor.
@@ -245,8 +267,13 @@
     arg_to_replace = expr(Source{{12, 34}}, *this);
 
     // Call the builtin with the constexpr argument replaced
-    Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call(overload.function, args)),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (expr.invalid_index == Constexpr::kValid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -281,7 +308,7 @@
     GlobalConst("G", nullptr, expr({}, *this));
 
     auto args = overload.args(this);
-    auto*& arg_to_replace = (param.position == Position::kFirst) ? args.front() : args.back();
+    auto*& arg_to_replace = (param.position == Position::kFirst) ? args.Front() : args.Back();
 
     // Make the expression to be replaced, reachable. This keeps the resolver
     // happy.
@@ -290,8 +317,13 @@
     arg_to_replace = Expr(Source{{12, 34}}, "G");
 
     // Call the builtin with the constexpr argument replaced
-    Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call(overload.function, args)),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     std::stringstream err;
@@ -386,10 +418,10 @@
     // fn func { return dot4I8Packed(1u, 2u); }
     Enable(ast::Extension::kChromiumExperimentalDp4A);
 
-    Func("func", {}, ty.i32(),
-         {
+    Func("func", utils::Empty, ty.i32(),
+         utils::Vector{
              Return(Call(Source{Source::Location{12, 34}}, "dot4I8Packed",
-                         ast::ExpressionList{Expr(1_u), Expr(2_u)})),
+                         utils::Vector{Expr(1_u), Expr(2_u)})),
          });
 
     EXPECT_TRUE(r()->Resolve());
@@ -397,10 +429,10 @@
 
 TEST_F(ResolverDP4aExtensionValidationTest, Dot4I8PackedWithoutExtension) {
     // fn func { return dot4I8Packed(1u, 2u); }
-    Func("func", {}, ty.i32(),
-         {
+    Func("func", utils::Empty, ty.i32(),
+         utils::Vector{
              Return(Call(Source{Source::Location{12, 34}}, "dot4I8Packed",
-                         ast::ExpressionList{Expr(1_u), Expr(2_u)})),
+                         utils::Vector{Expr(1_u), Expr(2_u)})),
          });
 
     EXPECT_FALSE(r()->Resolve());
@@ -414,10 +446,10 @@
     // fn func { return dot4U8Packed(1u, 2u); }
     Enable(ast::Extension::kChromiumExperimentalDp4A);
 
-    Func("func", {}, ty.u32(),
-         {
+    Func("func", utils::Empty, ty.u32(),
+         utils::Vector{
              Return(Call(Source{Source::Location{12, 34}}, "dot4U8Packed",
-                         ast::ExpressionList{Expr(1_u), Expr(2_u)})),
+                         utils::Vector{Expr(1_u), Expr(2_u)})),
          });
 
     EXPECT_TRUE(r()->Resolve());
@@ -425,10 +457,10 @@
 
 TEST_F(ResolverDP4aExtensionValidationTest, Dot4U8PackedWithoutExtension) {
     // fn func { return dot4U8Packed(1u, 2u); }
-    Func("func", {}, ty.u32(),
-         {
+    Func("func", utils::Empty, ty.u32(),
+         utils::Vector{
              Return(Call(Source{Source::Location{12, 34}}, "dot4U8Packed",
-                         ast::ExpressionList{Expr(1_u), Expr(2_u)})),
+                         utils::Vector{Expr(1_u), Expr(2_u)})),
          });
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index c3ec4a7..6318901 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -101,18 +101,26 @@
     const Params& params = GetParam();
 
     auto* p = GlobalVar("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
-    auto* input = Param("input", params.type(*this), {Builtin(Source{{12, 34}}, params.builtin)});
+    auto* input = Param("input", params.type(*this),
+                        utils::Vector{Builtin(Source{{12, 34}}, params.builtin)});
     switch (params.stage) {
         case ast::PipelineStage::kVertex:
-            Func("main", {input}, ty.vec4<f32>(), {Return(p)}, {Stage(ast::PipelineStage::kVertex)},
-                 {Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition)});
+            Func("main", utils::Vector{input}, ty.vec4<f32>(), utils::Vector{Return(p)},
+                 utils::Vector{Stage(ast::PipelineStage::kVertex)},
+                 utils::Vector{
+                     Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition),
+                 });
             break;
         case ast::PipelineStage::kFragment:
-            Func("main", {input}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)}, {});
+            Func("main", utils::Vector{input}, ty.void_(), utils::Empty,
+                 utils::Vector{
+                     Stage(ast::PipelineStage::kFragment),
+                 },
+                 {});
             break;
         case ast::PipelineStage::kCompute:
-            Func("main", {input}, ty.void_(), {},
-                 {
+            Func("main", utils::Vector{input}, ty.void_(), utils::Empty,
+                 utils::Vector{
                      Stage(ast::PipelineStage::kCompute),
                      WorkgroupSize(1_i),
                  });
@@ -141,17 +149,20 @@
     //   @builtin(frag_depth) fd: f32,
     // ) -> @location(0) f32 { return 1.0; }
     Func("fs_main",
-         {
-             Param("fd", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth)}),
+         utils::Vector{
+             Param("fd", ty.f32(),
+                   utils::Vector{
+                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth),
+                   }),
          },
          ty.f32(),
-         {
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -168,23 +179,25 @@
     // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
 
     auto* s = Structure("MyInputs",
-                        {
+                        utils::Vector{
                             Member("frag_depth", ty.f32(),
-                                   {Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth)}),
+                                   utils::Vector{
+                                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth),
+                                   }),
                         });
 
     Func("fragShader",
-         {
+         utils::Vector{
              Param("arg", ty.Of(s)),
          },
          ty.f32(),
-         {
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -201,10 +214,17 @@
     // @fragment
     // fn fragShader() { var s : S; }
 
-    Structure("S", {Member("idx", ty.u32(), {Builtin(ast::BuiltinValue::kVertexIndex)})});
+    Structure("S", utils::Vector{
+                       Member("idx", ty.u32(),
+                              utils::Vector{
+                                  Builtin(ast::BuiltinValue::kVertexIndex),
+                              }),
+                   });
 
-    Func("fragShader", {}, ty.void_(), {Decl(Var("s", ty.type_name("S")))},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("fragShader", utils::Empty, ty.void_(), utils::Vector{Decl(Var("s", ty.type_name("S")))},
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
     EXPECT_TRUE(r()->Resolve());
 }
 
@@ -217,23 +237,25 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s =
-        Structure("MyInputs", {
-                                  Member("position", ty.vec4<u32>(),
-                                         {Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition)}),
-                              });
+    auto* s = Structure("MyInputs",
+                        utils::Vector{
+                            Member("position", ty.vec4<u32>(),
+                                   utils::Vector{
+                                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition),
+                                   }),
+                        });
     Func("fragShader",
-         {
+         utils::Vector{
              Param("arg", ty.Of(s)),
          },
          ty.f32(),
-         {
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
 
@@ -244,8 +266,14 @@
 TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
     // @vertex
     // fn main() -> @builtin(position) f32 { return 1.0; }
-    Func("main", {}, ty.f32(), {Return(1_f)}, {Stage(ast::PipelineStage::kVertex)},
-         {Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition)});
+    Func("main", utils::Empty, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{Stage(ast::PipelineStage::kVertex)},
+         utils::Vector{
+             Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
@@ -259,18 +287,20 @@
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
     auto* s = Structure("MyInputs",
-                        {
+                        utils::Vector{
                             Member("frag_depth", ty.i32(),
-                                   {Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth)}),
+                                   utils::Vector{
+                                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth),
+                                   }),
                         });
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(),
-         {
+    Func("fragShader", utils::Vector{Param("arg", ty.Of(s))}, ty.f32(),
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
 
@@ -285,13 +315,23 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s = Structure(
-        "MyInputs",
-        {
-            Member("m", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleMask)}),
-        });
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    auto* s = Structure("MyInputs",
+                        utils::Vector{
+                            Member("m", ty.f32(),
+                                   utils::Vector{
+                                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleMask),
+                                   }),
+                        });
+    Func("fragShader", utils::Vector{Param("arg", ty.Of(s))}, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
@@ -300,8 +340,13 @@
 TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
     // @fragment
     // fn main() -> @builtin(sample_mask) i32 { return 1; }
-    Func("main", {}, ty.i32(), {Return(1_i)}, {Stage(ast::PipelineStage::kFragment)},
-         {Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleMask)});
+    Func("main", utils::Empty, ty.i32(), utils::Vector{Return(1_i)},
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleMask),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
@@ -313,17 +358,20 @@
     //   @builtin(sample_mask) arg: bool
     // ) -> @location(0) f32 { return 1.0; }
     Func("fs_main",
-         {
-             Param("arg", ty.bool_(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleMask)}),
+         utils::Vector{
+             Param("arg", ty.bool_(),
+                   utils::Vector{
+                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleMask),
+                   }),
          },
          ty.f32(),
-         {
+         utils::Vector{
              Return(1_f),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -337,13 +385,23 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s = Structure(
-        "MyInputs",
-        {
-            Member("m", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleIndex)}),
-        });
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    auto* s = Structure("MyInputs",
+                        utils::Vector{
+                            Member("m", ty.f32(),
+                                   utils::Vector{
+                                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleIndex),
+                                   }),
+                        });
+    Func("fragShader", utils::Vector{Param("arg", ty.Of(s))}, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
@@ -355,14 +413,20 @@
     //   @builtin(sample_index) arg: bool
     // ) -> @location(0) f32 { return 1.0; }
     Func("fs_main",
-         {
-             Param("arg", ty.bool_(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleIndex)}),
+         utils::Vector{
+             Param("arg", ty.bool_(),
+                   utils::Vector{
+                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kSampleIndex),
+                   }),
          },
-         ty.f32(), {Return(1_f)},
-         {
+         ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -375,14 +439,20 @@
     //   @builtin(kPosition) p: vec3<f32>,
     // ) -> @location(0) f32 { return 1.0; }
     Func("fs_main",
-         {
-             Param("p", ty.vec3<f32>(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition)}),
+         utils::Vector{
+             Param("p", ty.vec3<f32>(),
+                   utils::Vector{
+                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kPosition),
+                   }),
          },
-         ty.f32(), {Return(1_f)},
-         {
+         ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          },
-         {
+         utils::Vector{
              Location(0),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -393,8 +463,17 @@
     // @fragment
     // fn fs_main() -> @builtin(kFragDepth) f32 { var fd: i32; return fd; }
     auto* fd = Var("fd", ty.i32());
-    Func("fs_main", {}, ty.i32(), {Decl(fd), Return(fd)}, {Stage(ast::PipelineStage::kFragment)},
-         {Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth)});
+    Func("fs_main", utils::Empty, ty.i32(),
+         utils::Vector{
+             Decl(fd),
+             Return(fd),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Builtin(Source{{12, 34}}, ast::BuiltinValue::kFragDepth),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
 }
@@ -405,10 +484,21 @@
     //   @builtin(kVertexIndex) vi : f32,
     //   @builtin(kPosition) p :vec4<f32>
     // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
-    auto* p = Param("p", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)});
-    auto* vi = Param("vi", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kVertexIndex)});
-    Func("main", {vi, p}, ty.vec4<f32>(), {Return(Expr("p"))}, {Stage(ast::PipelineStage::kVertex)},
-         {Builtin(ast::BuiltinValue::kPosition)});
+    auto* p = Param("p", ty.vec4<f32>(),
+                    utils::Vector{
+                        Builtin(ast::BuiltinValue::kPosition),
+                    });
+    auto* vi = Param("vi", ty.f32(),
+                     utils::Vector{
+                         Builtin(Source{{12, 34}}, ast::BuiltinValue::kVertexIndex),
+                     });
+    Func("main", utils::Vector{vi, p}, ty.vec4<f32>(), utils::Vector{Return(Expr("p"))},
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         },
+         utils::Vector{
+             Builtin(ast::BuiltinValue::kPosition),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(vertex_index) must be 'u32'");
 }
@@ -419,11 +509,21 @@
     //   @builtin(kInstanceIndex) ii : f32,
     //   @builtin(kPosition) p :vec4<f32>
     // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
-    auto* p = Param("p", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)});
-    auto* ii =
-        Param("ii", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kInstanceIndex)});
-    Func("main", {ii, p}, ty.vec4<f32>(), {Return(Expr("p"))}, {Stage(ast::PipelineStage::kVertex)},
-         {Builtin(ast::BuiltinValue::kPosition)});
+    auto* p = Param("p", ty.vec4<f32>(),
+                    utils::Vector{
+                        Builtin(ast::BuiltinValue::kPosition),
+                    });
+    auto* ii = Param("ii", ty.f32(),
+                     utils::Vector{
+                         Builtin(Source{{12, 34}}, ast::BuiltinValue::kInstanceIndex),
+                     });
+    Func("main", utils::Vector{ii, p}, ty.vec4<f32>(), utils::Vector{Return(Expr("p"))},
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         },
+         utils::Vector{
+             Builtin(ast::BuiltinValue::kPosition),
+         });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(instance_index) must be 'u32'");
 }
@@ -436,13 +536,34 @@
     //   @builtin(sample_index) si: u32,
     //   @builtin(sample_mask) sm : u32
     // ) -> @builtin(frag_depth) f32 { var fd: f32; return fd; }
-    auto* p = Param("p", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)});
-    auto* ff = Param("ff", ty.bool_(), {Builtin(ast::BuiltinValue::kFrontFacing)});
-    auto* si = Param("si", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)});
-    auto* sm = Param("sm", ty.u32(), {Builtin(ast::BuiltinValue::kSampleMask)});
+    auto* p = Param("p", ty.vec4<f32>(),
+                    utils::Vector{
+                        Builtin(ast::BuiltinValue::kPosition),
+                    });
+    auto* ff = Param("ff", ty.bool_(),
+                     utils::Vector{
+                         Builtin(ast::BuiltinValue::kFrontFacing),
+                     });
+    auto* si = Param("si", ty.u32(),
+                     utils::Vector{
+                         Builtin(ast::BuiltinValue::kSampleIndex),
+                     });
+    auto* sm = Param("sm", ty.u32(),
+                     utils::Vector{
+                         Builtin(ast::BuiltinValue::kSampleMask),
+                     });
     auto* var_fd = Var("fd", ty.f32());
-    Func("fs_main", {p, ff, si, sm}, ty.f32(), {Decl(var_fd), Return(var_fd)},
-         {Stage(ast::PipelineStage::kFragment)}, {Builtin(ast::BuiltinValue::kFragDepth)});
+    Func("fs_main", utils::Vector{p, ff, si, sm}, ty.f32(),
+         utils::Vector{
+             Decl(var_fd),
+             Return(var_fd),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Builtin(ast::BuiltinValue::kFragDepth),
+         });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -452,17 +573,25 @@
     //   @builtin(vertex_index) vi : u32,
     //   @builtin(instance_index) ii : u32,
     // ) -> @builtin(position) vec4<f32> { var p :vec4<f32>; return p; }
-    auto* vi = Param("vi", ty.u32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kVertexIndex)});
+    auto* vi = Param("vi", ty.u32(),
+                     utils::Vector{
+                         Builtin(Source{{12, 34}}, ast::BuiltinValue::kVertexIndex),
+                     });
 
-    auto* ii =
-        Param("ii", ty.u32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kInstanceIndex)});
+    auto* ii = Param("ii", ty.u32(),
+                     utils::Vector{
+                         Builtin(Source{{12, 34}}, ast::BuiltinValue::kInstanceIndex),
+                     });
     auto* p = Var("p", ty.vec4<f32>());
-    Func("main", {vi, ii}, ty.vec4<f32>(),
-         {
+    Func("main", utils::Vector{vi, ii}, ty.vec4<f32>(),
+         utils::Vector{
              Decl(p),
              Return(p),
          },
-         {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::BuiltinValue::kPosition)});
+         utils::Vector{Stage(ast::PipelineStage::kVertex)},
+         utils::Vector{
+             Builtin(ast::BuiltinValue::kPosition),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -477,25 +606,42 @@
     //   @builtin(num_workgroups) nwgs: vec3<u32>,
     // ) {}
 
-    auto* li_id = Param("li_id", ty.vec3<u32>(), {Builtin(ast::BuiltinValue::kLocalInvocationId)});
-    auto* li_index =
-        Param("li_index", ty.u32(), {Builtin(ast::BuiltinValue::kLocalInvocationIndex)});
-    auto* gi = Param("gi", ty.vec3<u32>(), {Builtin(ast::BuiltinValue::kGlobalInvocationId)});
-    auto* wi = Param("wi", ty.vec3<u32>(), {Builtin(ast::BuiltinValue::kWorkgroupId)});
-    auto* nwgs = Param("nwgs", ty.vec3<u32>(), {Builtin(ast::BuiltinValue::kNumWorkgroups)});
+    auto* li_id = Param("li_id", ty.vec3<u32>(),
+                        utils::Vector{
+                            Builtin(ast::BuiltinValue::kLocalInvocationId),
+                        });
+    auto* li_index = Param("li_index", ty.u32(),
+                           utils::Vector{
+                               Builtin(ast::BuiltinValue::kLocalInvocationIndex),
+                           });
+    auto* gi = Param("gi", ty.vec3<u32>(),
+                     utils::Vector{
+                         Builtin(ast::BuiltinValue::kGlobalInvocationId),
+                     });
+    auto* wi = Param("wi", ty.vec3<u32>(),
+                     utils::Vector{
+                         Builtin(ast::BuiltinValue::kWorkgroupId),
+                     });
+    auto* nwgs = Param("nwgs", ty.vec3<u32>(),
+                       utils::Vector{
+                           Builtin(ast::BuiltinValue::kNumWorkgroups),
+                       });
 
-    Func("main", {li_id, li_index, gi, wi, nwgs}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    Func("main", utils::Vector{li_id, li_index, gi, wi, nwgs}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute),
+                       WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
-    auto* wi = Param("wi", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kWorkgroupId)});
-    Func("main", {wi}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    auto* wi = Param("wi", ty.f32(),
+                     utils::Vector{
+                         Builtin(Source{{12, 34}}, ast::BuiltinValue::kWorkgroupId),
+                     });
+    Func("main", utils::Vector{wi}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute),
+                       WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -504,11 +650,13 @@
 }
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_NumWorkgroupsNotVec3U32) {
-    auto* nwgs =
-        Param("nwgs", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kNumWorkgroups)});
-    Func("main", {nwgs}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+    auto* nwgs = Param("nwgs", ty.f32(),
+                       utils::Vector{
+                           Builtin(Source{{12, 34}}, ast::BuiltinValue::kNumWorkgroups),
+                       });
+    Func("main", utils::Vector{nwgs}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute),
+                       WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -518,10 +666,12 @@
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32) {
     auto* gi = Param("gi", ty.vec3<i32>(),
-                     {Builtin(Source{{12, 34}}, ast::BuiltinValue::kGlobalInvocationId)});
-    Func("main", {gi}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+                     utils::Vector{
+                         Builtin(Source{{12, 34}}, ast::BuiltinValue::kGlobalInvocationId),
+                     });
+    Func("main", utils::Vector{gi}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute),
+                       WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -531,10 +681,12 @@
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32) {
     auto* li_index = Param("li_index", ty.vec3<u32>(),
-                           {Builtin(Source{{12, 34}}, ast::BuiltinValue::kLocalInvocationIndex)});
-    Func("main", {li_index}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+                           utils::Vector{
+                               Builtin(Source{{12, 34}}, ast::BuiltinValue::kLocalInvocationIndex),
+                           });
+    Func("main", utils::Vector{li_index}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute),
+                       WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -544,10 +696,12 @@
 
 TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32) {
     auto* li_id = Param("li_id", ty.vec2<u32>(),
-                        {Builtin(Source{{12, 34}}, ast::BuiltinValue::kLocalInvocationId)});
-    Func("main", {li_id}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
+                        utils::Vector{
+                            Builtin(Source{{12, 34}}, ast::BuiltinValue::kLocalInvocationId),
+                        });
+    Func("main", utils::Vector{li_id}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute),
+                       WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2_i))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -565,13 +719,34 @@
     // @fragment
     // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s = Structure(
-        "MyInputs", {Member("position", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)}),
-                     Member("front_facing", ty.bool_(), {Builtin(ast::BuiltinValue::kFrontFacing)}),
-                     Member("sample_index", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)}),
-                     Member("sample_mask", ty.u32(), {Builtin(ast::BuiltinValue::kSampleMask)})});
-    Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    auto* s = Structure("MyInputs", utils::Vector{
+                                        Member("position", ty.vec4<f32>(),
+                                               utils::Vector{
+                                                   Builtin(ast::BuiltinValue::kPosition),
+                                               }),
+                                        Member("front_facing", ty.bool_(),
+                                               utils::Vector{
+                                                   Builtin(ast::BuiltinValue::kFrontFacing),
+                                               }),
+                                        Member("sample_index", ty.u32(),
+                                               utils::Vector{
+                                                   Builtin(ast::BuiltinValue::kSampleIndex),
+                                               }),
+                                        Member("sample_mask", ty.u32(),
+                                               utils::Vector{
+                                                   Builtin(ast::BuiltinValue::kSampleMask),
+                                               }),
+                                    });
+    Func("fragShader", utils::Vector{Param("arg", ty.Of(s))}, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -581,10 +756,20 @@
     //   @builtin(front_facing) is_front: i32;
     // ) -> @location(0) f32 { return 1.0; }
 
-    auto* is_front =
-        Param("is_front", ty.i32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kFrontFacing)});
-    Func("fs_main", {is_front}, ty.f32(), {Return(1_f)}, {Stage(ast::PipelineStage::kFragment)},
-         {Location(0)});
+    auto* is_front = Param("is_front", ty.i32(),
+                           utils::Vector{
+                               Builtin(Source{{12, 34}}, ast::BuiltinValue::kFrontFacing),
+                           });
+    Func("fs_main", utils::Vector{is_front}, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
@@ -597,11 +782,23 @@
     // @fragment
     // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
 
-    auto* s = Structure(
-        "MyInputs",
-        {Member("pos", ty.f32(), {Builtin(Source{{12, 34}}, ast::BuiltinValue::kFrontFacing)})});
-    Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    auto* s = Structure("MyInputs",
+                        utils::Vector{
+                            Member("pos", ty.f32(),
+                                   utils::Vector{
+                                       Builtin(Source{{12, 34}}, ast::BuiltinValue::kFrontFacing),
+                                   }),
+                        });
+    Func("fragShader", utils::Vector{Param("is_front", ty.Of(s))}, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
@@ -900,13 +1097,18 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(Expr(1_f));
+        params.Push(Expr(1_f));
     }
     auto* builtin = Call(name, params);
-    Func("func", {}, ty.void_(), {CallStmt(builtin)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(builtin),
+         },
+         utils::Vector{
+             create<ast::StageAttribute>(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_TRUE(TypeOf(builtin)->Is<sem::F32>());
@@ -916,13 +1118,18 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec2<f32>(1_f, 1_f));
+        params.Push(vec2<f32>(1_f, 1_f));
     }
     auto* builtin = Call(name, params);
-    Func("func", {}, ty.void_(), {CallStmt(builtin)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(builtin),
+         },
+         utils::Vector{
+             create<ast::StageAttribute>(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
@@ -932,13 +1139,18 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec3<f32>(1_f, 1_f, 1_f));
+        params.Push(vec3<f32>(1_f, 1_f, 1_f));
     }
     auto* builtin = Call(name, params);
-    Func("func", {}, ty.void_(), {CallStmt(builtin)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(builtin),
+         },
+         utils::Vector{
+             create<ast::StageAttribute>(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
@@ -948,13 +1160,18 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec4<f32>(1_f, 1_f, 1_f, 1_f));
+        params.Push(vec4<f32>(1_f, 1_f, 1_f, 1_f));
     }
     auto* builtin = Call(name, params);
-    Func("func", {}, ty.void_(), {CallStmt(builtin)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(builtin),
+         },
+         utils::Vector{
+             create<ast::StageAttribute>(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
@@ -1010,9 +1227,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(Construct<u32>(1_i));
+        params.Push(Construct<u32>(1_i));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1025,9 +1242,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec2<u32>(1_u, 1_u));
+        params.Push(vec2<u32>(1_u, 1_u));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1040,9 +1257,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec3<u32>(1_u, 1_u, 1_u));
+        params.Push(vec3<u32>(1_u, 1_u, 1_u));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1055,9 +1272,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec4<u32>(1_u, 1_u, 1_u, 1_u));
+        params.Push(vec4<u32>(1_u, 1_u, 1_u, 1_u));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1070,9 +1287,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(Construct<i32>(1_i));
+        params.Push(Construct<i32>(1_i));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1085,9 +1302,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec2<i32>(1_i, 1_i));
+        params.Push(vec2<i32>(1_i, 1_i));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1100,9 +1317,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec3<i32>(1_i, 1_i, 1_i));
+        params.Push(vec3<i32>(1_i, 1_i, 1_i));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1115,9 +1332,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec4<i32>(1_i, 1_i, 1_i, 1_i));
+        params.Push(vec4<i32>(1_i, 1_i, 1_i, 1_i));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1142,9 +1359,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec2<bool>(true, true));
+        params.Push(vec2<bool>(true, true));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1156,9 +1373,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec3<bool>(true, true, true));
+        params.Push(vec3<bool>(true, true, true));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
@@ -1170,9 +1387,9 @@
     std::string name = std::get<0>(GetParam());
     uint32_t num_params = std::get<1>(GetParam());
 
-    ast::ExpressionList params;
+    utils::Vector<const ast::Expression*, 8> params;
     for (uint32_t i = 0; i < num_params; ++i) {
-        params.push_back(vec4<bool>(true, true, true, true));
+        params.Push(vec4<bool>(true, true, true, true));
     }
     auto* builtin = Call(name, params);
     WrapInFunction(builtin);
diff --git a/src/tint/resolver/call_test.cc b/src/tint/resolver/call_test.cc
index 3020254..37aaffa 100644
--- a/src/tint/resolver/call_test.cc
+++ b/src/tint/resolver/call_test.cc
@@ -84,14 +84,14 @@
 TEST_F(ResolverCallTest, Valid) {
     Enable(ast::Extension::kF16);
 
-    ast::ParameterList params;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Parameter*, 4> params;
+    utils::Vector<const ast::Expression*, 4> args;
     for (auto& p : all_param_types) {
-        params.push_back(Param(Sym(), p.create_type(*this)));
-        args.push_back(p.create_value(*this, 0));
+        params.Push(Param(Sym(), p.create_type(*this)));
+        args.Push(p.create_value(*this, 0));
     }
 
-    auto* func = Func("foo", std::move(params), ty.f32(), {Return(1.23_f)});
+    auto* func = Func("foo", std::move(params), ty.f32(), utils::Vector{Return(1.23_f)});
     auto* call_expr = Call("foo", std::move(args));
     WrapInFunction(call_expr);
 
@@ -104,8 +104,8 @@
 
 TEST_F(ResolverCallTest, OutOfOrder) {
     auto* call_expr = Call("b");
-    Func("a", {}, ty.void_(), {CallStmt(call_expr)});
-    auto* b = Func("b", {}, ty.void_(), {});
+    Func("a", utils::Empty, ty.void_(), utils::Vector{CallStmt(call_expr)});
+    auto* b = Func("b", utils::Empty, ty.void_(), utils::Empty);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 9625be7..4243093 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -26,7 +26,15 @@
 using ResolverCallValidationTest = ResolverTest;
 
 TEST_F(ResolverCallValidationTest, TooFewArgs) {
-    Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), {Return()});
+    Func("foo",
+         utils::Vector{
+             Param(Sym(), ty.i32()),
+             Param(Sym(), ty.f32()),
+         },
+         ty.void_(),
+         utils::Vector{
+             Return(),
+         });
     auto* call = Call(Source{{12, 34}}, "foo", 1_i);
     WrapInFunction(call);
 
@@ -35,7 +43,15 @@
 }
 
 TEST_F(ResolverCallValidationTest, TooManyArgs) {
-    Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), {Return()});
+    Func("foo",
+         utils::Vector{
+             Param(Sym(), ty.i32()),
+             Param(Sym(), ty.f32()),
+         },
+         ty.void_(),
+         utils::Vector{
+             Return(),
+         });
     auto* call = Call(Source{{12, 34}}, "foo", 1_i, 1_f, 1_f);
     WrapInFunction(call);
 
@@ -44,7 +60,15 @@
 }
 
 TEST_F(ResolverCallValidationTest, MismatchedArgs) {
-    Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), {Return()});
+    Func("foo",
+         utils::Vector{
+             Param(Sym(), ty.i32()),
+             Param(Sym(), ty.f32()),
+         },
+         ty.void_(),
+         utils::Vector{
+             Return(),
+         });
     auto* call = Call("foo", Expr(Source{{12, 34}}, true), 1_f);
     WrapInFunction(call);
 
@@ -58,10 +82,14 @@
     // fn func() -> f32 { return 1.0; }
     // fn main() {func(); return; }
 
-    Func("func", {}, ty.f32(), {Return(Expr(1_f))}, {});
+    Func("func", utils::Empty, ty.f32(),
+         utils::Vector{
+             Return(Expr(1_f)),
+         },
+         utils::Empty);
 
-    Func("main", {}, ty.void_(),
-         {
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              CallStmt(Source{{12, 34}}, Call("func")),
              Return(),
          });
@@ -76,9 +104,9 @@
     //   foo(&z);
     // }
     auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
-    Func("foo", {param}, ty.void_(), {});
-    Func("main", {}, ty.void_(),
-         {
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("z", ty.i32(), Expr(1_i))),
              CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Expr("z")))),
          });
@@ -93,9 +121,9 @@
     //   foo(&z);
     // }
     auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
-    Func("foo", {param}, ty.void_(), {});
-    Func("main", {}, ty.void_(),
-         {
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Let("z", ty.i32(), Expr(1_i))),
              CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
          });
@@ -111,11 +139,13 @@
     //   var v: S;
     //   foo(&v.m);
     // }
-    auto* S = Structure("S", {Member("m", ty.i32())});
+    auto* S = Structure("S", utils::Vector{
+                                 Member("m", ty.i32()),
+                             });
     auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
-    Func("foo", {param}, ty.void_(), {});
-    Func("main", {}, ty.void_(),
-         {
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("v", ty.Of(S))),
              CallStmt(Call("foo", AddressOf(Source{{12, 34}}, MemberAccessor("v", "m")))),
          });
@@ -133,11 +163,13 @@
     //   let v: S = S();
     //   foo(&v.m);
     // }
-    auto* S = Structure("S", {Member("m", ty.i32())});
+    auto* S = Structure("S", utils::Vector{
+                                 Member("m", ty.i32()),
+                             });
     auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
-    Func("foo", {param}, ty.void_(), {});
-    Func("main", {}, ty.void_(),
-         {
+    Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Let("v", ty.Of(S), Construct(ty.Of(S)))),
              CallStmt(Call("foo", AddressOf(MemberAccessor(Source{{12, 34}}, "v", "m")))),
          });
@@ -151,9 +183,19 @@
     // fn bar(p: ptr<function, i32>) {
     // foo(p);
     // }
-    Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
-    Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(),
-         ast::StatementList{CallStmt(Call("foo", Expr("p")))});
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("bar",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             CallStmt(Call("foo", Expr("p"))),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -168,15 +210,25 @@
     //   var v: i32;
     //   bar(&v);
     // }
-    Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
-    Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(),
-         ast::StatementList{CallStmt(Call("foo", Expr("p")))});
-    Func("main", {}, ty.void_(),
-         {
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+         },
+         ty.void_(), utils::Empty);
+    Func("bar",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+         },
+         ty.void_(),
+         utils::Vector{
+             CallStmt(Call("foo", Expr("p"))),
+         });
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("v", ty.i32(), Expr(1_i))),
              CallStmt(Call("foo", AddressOf(Expr("v")))),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
 
@@ -191,17 +243,21 @@
     //   let p: ptr<function, i32> = &v;
     //   var c: i32 = x(p);
     // }
-    Func("x", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
+    Func("x",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+         },
+         ty.void_(), utils::Empty);
     auto* v = Var("v", ty.i32());
     auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(v));
     auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("x", Expr(Source{{12, 34}}, p)));
-    Func("main", {}, ty.void_(),
-         {
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(v),
              Decl(p),
              Decl(c),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -218,16 +274,20 @@
     // fn main() {
     //   var c: i32 = foo(p);
     // }
-    Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kPrivate))}, ty.void_(), {});
+    Func("foo",
+         utils::Vector{
+             Param("p", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+         },
+         ty.void_(), utils::Empty);
     auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(v));
     auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("foo", Expr(Source{{12, 34}}, p)));
-    Func("main", {}, ty.void_(),
-         {
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(p),
              Decl(c),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kFragment),
          });
     EXPECT_FALSE(r()->Resolve());
@@ -242,7 +302,10 @@
     //   v();
     // }
     GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    Func("f", {}, ty.void_(), {CallStmt(Call(Source{{12, 34}}, "v"))});
+    Func("f", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call(Source{{12, 34}}, "v")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(error: cannot call variable 'v'
@@ -255,9 +318,9 @@
     //   var x : i32;
     //   x();
     // }
-    Func("x", {}, ty.void_(), {});
-    Func("f", {}, ty.void_(),
-         {
+    Func("x", utils::Empty, ty.void_(), utils::Empty);
+    Func("f", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var(Source{{56, 78}}, "x", ty.i32())),
              CallStmt(Call(Source{{12, 34}}, "x")),
          });
diff --git a/src/tint/resolver/compound_statement_test.cc b/src/tint/resolver/compound_statement_test.cc
index d962b3e..0a96ced 100644
--- a/src/tint/resolver/compound_statement_test.cc
+++ b/src/tint/resolver/compound_statement_test.cc
@@ -35,7 +35,7 @@
     //   var x : 32;
     // }
     auto* stmt = Decl(Var("x", ty.i32()));
-    auto* f = Func("F", {}, ty.void_(), {stmt});
+    auto* f = Func("F", utils::Empty, ty.void_(), utils::Vector{stmt});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -57,7 +57,7 @@
     // }
     auto* stmt = Decl(Var("x", ty.i32()));
     auto* block = Block(stmt);
-    auto* f = Func("F", {}, ty.void_(), {block});
+    auto* f = Func("F", utils::Empty, ty.void_(), utils::Vector{block});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -92,7 +92,7 @@
     auto* brk = Break();
     auto* stmt = Ignore(1_i);
     auto* loop = Loop(Block(brk), Block(stmt));
-    auto* f = Func("F", {}, ty.void_(), {loop});
+    auto* f = Func("F", utils::Empty, ty.void_(), utils::Vector{loop});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -155,7 +155,7 @@
     // }
     auto* brk = Break();
     auto* loop = Loop(Block(brk), Block());
-    Func("F", {}, ty.void_(), {loop});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{loop});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -186,7 +186,7 @@
     auto* stmt = Return();
     auto* body = Block(stmt);
     auto* for_ = For(init, cond, cont, body);
-    auto* f = Func("F", {}, ty.void_(), {for_});
+    auto* f = Func("F", utils::Empty, ty.void_(), utils::Vector{for_});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -250,7 +250,7 @@
     auto* stmt = Return();
     auto* body = Block(stmt);
     auto* while_ = While(cond, body);
-    auto* f = Func("W", {}, ty.void_(), {while_});
+    auto* f = Func("W", utils::Empty, ty.void_(), utils::Vector{while_});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/const_eval_test.cc b/src/tint/resolver/const_eval_test.cc
index 3a6c971..6bc07b0 100644
--- a/src/tint/resolver/const_eval_test.cc
+++ b/src/tint/resolver/const_eval_test.cc
@@ -1771,7 +1771,7 @@
 }
 
 TEST_F(ResolverConstEvalTest, Array_Struct_f32_Zero) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.f32()),
                        Member("m2", ty.f32()),
                    });
@@ -1914,7 +1914,7 @@
 }
 
 TEST_F(ResolverConstEvalTest, Array_Struct_f32_Elements) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.f32()),
                        Member("m2", ty.f32()),
                    });
@@ -1958,7 +1958,8 @@
 }
 
 TEST_F(ResolverConstEvalTest, Struct_I32s_ZeroInit) {
-    Structure("S", {Member("m1", ty.i32()), Member("m2", ty.i32()), Member("m3", ty.i32())});
+    Structure(
+        "S", utils::Vector{Member("m1", ty.i32()), Member("m2", ty.i32()), Member("m3", ty.i32())});
     auto* expr = Construct(ty.type_name("S"));
     WrapInFunction(expr);
 
@@ -1997,7 +1998,7 @@
 TEST_F(ResolverConstEvalTest, Struct_MixedScalars_ZeroInit) {
     Enable(ast::Extension::kF16);
 
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.i32()),
                        Member("m2", ty.u32()),
                        Member("m3", ty.f32()),
@@ -2052,7 +2053,7 @@
 }
 
 TEST_F(ResolverConstEvalTest, Struct_VectorF32s_ZeroInit) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.vec3<f32>()),
                        Member("m2", ty.vec3<f32>()),
                        Member("m3", ty.vec3<f32>()),
@@ -2104,7 +2105,7 @@
 TEST_F(ResolverConstEvalTest, Struct_MixedVectors_ZeroInit) {
     Enable(ast::Extension::kF16);
 
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.vec2<i32>()),
                        Member("m2", ty.vec3<u32>()),
                        Member("m3", ty.vec4<f32>()),
@@ -2173,13 +2174,13 @@
 }
 
 TEST_F(ResolverConstEvalTest, Struct_Struct_ZeroInit) {
-    Structure("Inner", {
+    Structure("Inner", utils::Vector{
                            Member("m1", ty.i32()),
                            Member("m2", ty.u32()),
                            Member("m3", ty.f32()),
                        });
 
-    Structure("Outer", {
+    Structure("Outer", utils::Vector{
                            Member("m1", ty.type_name("Inner")),
                            Member("m2", ty.type_name("Inner")),
                        });
@@ -2219,7 +2220,7 @@
 TEST_F(ResolverConstEvalTest, Struct_MixedScalars_Construct) {
     Enable(ast::Extension::kF16);
 
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.i32()),
                        Member("m2", ty.u32()),
                        Member("m3", ty.f32()),
@@ -2276,7 +2277,7 @@
 TEST_F(ResolverConstEvalTest, Struct_MixedVectors_Construct) {
     Enable(ast::Extension::kF16);
 
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.vec2<i32>()),
                        Member("m2", ty.vec3<u32>()),
                        Member("m3", ty.vec4<f32>()),
@@ -2346,13 +2347,13 @@
 }
 
 TEST_F(ResolverConstEvalTest, Struct_Struct_Construct) {
-    Structure("Inner", {
+    Structure("Inner", utils::Vector{
                            Member("m1", ty.i32()),
                            Member("m2", ty.u32()),
                            Member("m3", ty.f32()),
                        });
 
-    Structure("Outer", {
+    Structure("Outer", utils::Vector{
                            Member("m1", ty.type_name("Inner")),
                            Member("m2", ty.type_name("Inner")),
                        });
@@ -2392,7 +2393,7 @@
 }
 
 TEST_F(ResolverConstEvalTest, Struct_Array_Construct) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("m1", ty.array<i32, 2>()),
                        Member("m2", ty.array<f32, 3>()),
                    });
@@ -2921,13 +2922,13 @@
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 TEST_F(ResolverConstEvalTest, MemberAccess) {
-    Structure("Inner", {
+    Structure("Inner", utils::Vector{
                            Member("i1", ty.i32()),
                            Member("i2", ty.u32()),
                            Member("i3", ty.f32()),
                        });
 
-    Structure("Outer", {
+    Structure("Outer", utils::Vector{
                            Member("o1", ty.type_name("Inner")),
                            Member("o2", ty.type_name("Inner")),
                        });
@@ -3118,7 +3119,7 @@
 
 template <typename T>
 struct Values {
-    std::vector<T> args;
+    utils::Vector<T, 8> args;
     T result;
     bool result_pos_or_neg;
 };
@@ -3132,7 +3133,7 @@
     std::visit(
         [&](auto&& v) {
             for (auto& e : v.args) {
-                o << e << ((&e != &v.args.back()) ? " " : "");
+                o << e << ((&e != &v.args.Back()) ? " " : "");
             }
         },
         c.values);
@@ -3140,7 +3141,7 @@
 }
 
 template <typename T>
-Case C(std::vector<T> args, T result, bool result_pos_or_neg = false) {
+Case C(std::initializer_list<T> args, T result, bool result_pos_or_neg = false) {
     return Case{Values<T>{std::move(args), result, result_pos_or_neg}};
 }
 
diff --git a/src/tint/resolver/control_block_validation_test.cc b/src/tint/resolver/control_block_validation_test.cc
index a5ba7ca..9b0d289 100644
--- a/src/tint/resolver/control_block_validation_test.cc
+++ b/src/tint/resolver/control_block_validation_test.cc
@@ -213,8 +213,8 @@
     // }
     auto* var = Var("a", ty.i32(), Expr(2_i));
 
-    auto* block = Block(Decl(var), Switch("a",                                  //
-                                          Case(Source{{12, 34}}, {Expr(1_u)}),  //
+    auto* block = Block(Decl(var), Switch("a",                                               //
+                                          Case(Source{{12, 34}}, utils::Vector{Expr(1_u)}),  //
                                           DefaultCase()));
     WrapInFunction(block);
 
@@ -232,9 +232,9 @@
     // }
     auto* var = Var("a", ty.u32(), Expr(2_u));
 
-    auto* block = Block(Decl(var),                                    //
-                        Switch("a",                                   //
-                               Case(Source{{12, 34}}, {Expr(-1_i)}),  //
+    auto* block = Block(Decl(var),                                                 //
+                        Switch("a",                                                //
+                               Case(Source{{12, 34}}, utils::Vector{Expr(-1_i)}),  //
                                DefaultCase()));
     WrapInFunction(block);
 
@@ -256,7 +256,7 @@
     auto* block = Block(Decl(var),   //
                         Switch("a",  //
                                Case(Expr(0_u)),
-                               Case({
+                               Case(utils::Vector{
                                    Expr(Source{{12, 34}}, 2_u),
                                    Expr(3_u),
                                    Expr(Source{{56, 78}}, 2_u),
@@ -282,7 +282,7 @@
     auto* block = Block(Decl(var),   //
                         Switch("a",  //
                                Case(Expr(Source{{12, 34}}, -10_i)),
-                               Case({
+                               Case(utils::Vector{
                                    Expr(0_i),
                                    Expr(1_i),
                                    Expr(2_i),
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 3d550ff..36bff3f 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -237,7 +237,7 @@
 
     /// Traverses the statements, performing symbol resolution and determining
     /// global dependencies.
-    void TraverseStatements(const ast::StatementList& stmts) {
+    void TraverseStatements(utils::VectorRef<const ast::Statement*> stmts) {
         for (auto* s : stmts) {
             TraverseStatement(s);
         }
@@ -404,7 +404,7 @@
 
     /// Traverses the attribute list, performing symbol resolution and
     /// determining global dependencies.
-    void TraverseAttributes(const ast::AttributeList& attrs) {
+    void TraverseAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
         for (auto* attr : attrs) {
             TraverseAttribute(attr);
         }
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index b7ffa79..7a8110b 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -384,13 +384,13 @@
     /// The program builder
     ProgramBuilder* const builder;
     /// Parameters to a function that may need to be built
-    ast::ParameterList parameters;
+    utils::Vector<const ast::Parameter*, 8> parameters;
     /// Shallow function var / let declaration statements
-    ast::StatementList statements;
+    utils::Vector<const ast::Statement*, 8> statements;
     /// Nested function local var / let declaration statements
-    ast::StatementList nested_statements;
+    utils::Vector<const ast::Statement*, 8> nested_statements;
     /// Function attributes
-    ast::AttributeList func_attrs;
+    utils::Vector<const ast::Attribute*, 8> func_attrs;
 
     /// Constructor
     /// @param builder the program builder
@@ -431,32 +431,32 @@
         case SymbolDeclKind::Alias:
             return b.Alias(source, symbol, b.ty.i32());
         case SymbolDeclKind::Struct:
-            return b.Structure(source, symbol, {b.Member("m", b.ty.i32())});
+            return b.Structure(source, symbol, utils::Vector{b.Member("m", b.ty.i32())});
         case SymbolDeclKind::Function:
-            return b.Func(source, symbol, {}, b.ty.void_(), {});
+            return b.Func(source, symbol, utils::Empty, b.ty.void_(), utils::Empty);
         case SymbolDeclKind::Parameter: {
             auto* node = b.Param(source, symbol, b.ty.i32());
-            parameters.emplace_back(node);
+            parameters.Push(node);
             return node;
         }
         case SymbolDeclKind::LocalVar: {
             auto* node = b.Var(source, symbol, b.ty.i32());
-            statements.emplace_back(b.Decl(node));
+            statements.Push(b.Decl(node));
             return node;
         }
         case SymbolDeclKind::LocalLet: {
             auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1_i));
-            statements.emplace_back(b.Decl(node));
+            statements.Push(b.Decl(node));
             return node;
         }
         case SymbolDeclKind::NestedLocalVar: {
             auto* node = b.Var(source, symbol, b.ty.i32());
-            nested_statements.emplace_back(b.Decl(node));
+            nested_statements.Push(b.Decl(node));
             return node;
         }
         case SymbolDeclKind::NestedLocalLet: {
             auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1_i));
-            nested_statements.emplace_back(b.Decl(node));
+            nested_statements.Push(b.Decl(node));
             return node;
         }
     }
@@ -543,83 +543,82 @@
         }
         case SymbolUseKind::StructMemberType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Structure(b.Sym(), {b.Member("m", node)});
+            b.Structure(b.Sym(), utils::Vector{b.Member("m", node)});
             return node;
         }
         case SymbolUseKind::CallFunction: {
             auto* node = b.Expr(source, symbol);
-            statements.emplace_back(b.CallStmt(b.Call(node)));
+            statements.Push(b.CallStmt(b.Call(node)));
             return node;
         }
         case SymbolUseKind::ParameterType: {
             auto* node = b.ty.type_name(source, symbol);
-            parameters.emplace_back(b.Param(b.Sym(), node));
+            parameters.Push(b.Param(b.Sym(), node));
             return node;
         }
         case SymbolUseKind::LocalVarType: {
             auto* node = b.ty.type_name(source, symbol);
-            statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
+            statements.Push(b.Decl(b.Var(b.Sym(), node)));
             return node;
         }
         case SymbolUseKind::LocalVarArrayElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.array(node, 4_u), b.Expr(1_i))));
+            statements.Push(b.Decl(b.Var(b.Sym(), b.ty.array(node, 4_u), b.Expr(1_i))));
             return node;
         }
         case SymbolUseKind::LocalVarArraySizeValue: {
             auto* node = b.Expr(source, symbol);
-            statements.emplace_back(
-                b.Decl(b.Var(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1_i))));
+            statements.Push(b.Decl(b.Var(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1_i))));
             return node;
         }
         case SymbolUseKind::LocalVarVectorElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.vec3(node))));
+            statements.Push(b.Decl(b.Var(b.Sym(), b.ty.vec3(node))));
             return node;
         }
         case SymbolUseKind::LocalVarMatrixElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.mat3x4(node))));
+            statements.Push(b.Decl(b.Var(b.Sym(), b.ty.mat3x4(node))));
             return node;
         }
         case SymbolUseKind::LocalVarValue: {
             auto* node = b.Expr(source, symbol);
-            statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
+            statements.Push(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
             return node;
         }
         case SymbolUseKind::LocalLetType: {
             auto* node = b.ty.type_name(source, symbol);
-            statements.emplace_back(b.Decl(b.Let(b.Sym(), node, b.Expr(1_i))));
+            statements.Push(b.Decl(b.Let(b.Sym(), node, b.Expr(1_i))));
             return node;
         }
         case SymbolUseKind::LocalLetValue: {
             auto* node = b.Expr(source, symbol);
-            statements.emplace_back(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
+            statements.Push(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
             return node;
         }
         case SymbolUseKind::NestedLocalVarType: {
             auto* node = b.ty.type_name(source, symbol);
-            nested_statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
+            nested_statements.Push(b.Decl(b.Var(b.Sym(), node)));
             return node;
         }
         case SymbolUseKind::NestedLocalVarValue: {
             auto* node = b.Expr(source, symbol);
-            nested_statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
+            nested_statements.Push(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
             return node;
         }
         case SymbolUseKind::NestedLocalLetType: {
             auto* node = b.ty.type_name(source, symbol);
-            nested_statements.emplace_back(b.Decl(b.Let(b.Sym(), node, b.Expr(1_i))));
+            nested_statements.Push(b.Decl(b.Let(b.Sym(), node, b.Expr(1_i))));
             return node;
         }
         case SymbolUseKind::NestedLocalLetValue: {
             auto* node = b.Expr(source, symbol);
-            nested_statements.emplace_back(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
+            nested_statements.Push(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
             return node;
         }
         case SymbolUseKind::WorkgroupSizeValue: {
             auto* node = b.Expr(source, symbol);
-            func_attrs.emplace_back(b.WorkgroupSize(1_i, node, 2_i));
+            func_attrs.Push(b.WorkgroupSize(1_i, node, 2_i));
             return node;
         }
     }
@@ -628,15 +627,15 @@
 
 void SymbolTestHelper::Build() {
     auto& b = *builder;
-    if (!nested_statements.empty()) {
-        statements.emplace_back(b.Block(nested_statements));
-        nested_statements.clear();
+    if (!nested_statements.IsEmpty()) {
+        statements.Push(b.Block(nested_statements));
+        nested_statements.Clear();
     }
-    if (!parameters.empty() || !statements.empty() || !func_attrs.empty()) {
+    if (!parameters.IsEmpty() || !statements.IsEmpty() || !func_attrs.IsEmpty()) {
         b.Func("func", parameters, b.ty.void_(), statements, func_attrs);
-        parameters.clear();
-        statements.clear();
-        func_attrs.clear();
+        parameters.Clear();
+        statements.Clear();
+        func_attrs.Clear();
     }
 }
 
@@ -651,8 +650,8 @@
     // fn A() { B(); }
     // fn B() {}
 
-    Func("A", {}, ty.void_(), {CallStmt(Call(Expr(Source{{12, 34}}, "B")))});
-    Func(Source{{56, 78}}, "B", {}, ty.void_(), {Return()});
+    Func("A", utils::Empty, ty.void_(), utils::Vector{CallStmt(Call(Expr(Source{{12, 34}}, "B")))});
+    Func(Source{{56, 78}}, "B", utils::Empty, ty.void_(), utils::Vector{Return()});
 
     Build();
 }
@@ -663,7 +662,8 @@
     // }
     // type T = i32;
 
-    Func("F", {}, ty.void_(), {Block(Ignore(Construct(ty.type_name(Source{{12, 34}}, "T"))))});
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{Block(Ignore(Construct(ty.type_name(Source{{12, 34}}, "T"))))});
     Alias(Source{{56, 78}}, "T", ty.i32());
 
     Build();
@@ -675,7 +675,8 @@
     // }
     // type T = i32;
 
-    Func("F", {}, ty.void_(), {Block(Decl(Var("v", ty.type_name(Source{{12, 34}}, "T"))))});
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{Block(Decl(Var("v", ty.type_name(Source{{12, 34}}, "T"))))});
     Alias(Source{{56, 78}}, "T", ty.i32());
 
     Build();
@@ -685,7 +686,8 @@
     // fn F(p : T) {}
     // type T = i32;
 
-    Func("F", {Param("p", ty.type_name(Source{{12, 34}}, "T"))}, ty.void_(), {});
+    Func("F", utils::Vector{Param("p", ty.type_name(Source{{12, 34}}, "T"))}, ty.void_(),
+         utils::Empty);
     Alias(Source{{56, 78}}, "T", ty.i32());
 
     Build();
@@ -695,7 +697,7 @@
     // fn F() -> T {}
     // type T = i32;
 
-    Func("F", {}, ty.type_name(Source{{12, 34}}, "T"), {});
+    Func("F", utils::Empty, ty.type_name(Source{{12, 34}}, "T"), utils::Empty);
     Alias(Source{{56, 78}}, "T", ty.i32());
 
     Build();
@@ -705,7 +707,7 @@
     // struct S { m : T };
     // type T = i32;
 
-    Structure("S", {Member("m", ty.type_name(Source{{12, 34}}, "T"))});
+    Structure("S", utils::Vector{Member("m", ty.type_name(Source{{12, 34}}, "T"))});
     Alias(Source{{56, 78}}, "T", ty.i32());
 
     Build();
@@ -717,8 +719,8 @@
     // }
     // var G: f32 = 2.1;
 
-    Func("F", {}, ty.void_(),
-         {
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{
              Block(Assign(Expr(Source{{12, 34}}, "G"), 3.14_f)),
          });
 
@@ -808,8 +810,8 @@
 TEST_F(ResolverDependencyGraphCyclicRefTest, DirectCall) {
     // fn main() { main(); }
 
-    Func(Source{{12, 34}}, "main", {}, ty.void_(),
-         {CallStmt(Call(Expr(Source{{56, 78}}, "main")))});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.void_(),
+         utils::Vector{CallStmt(Call(Expr(Source{{56, 78}}, "main")))});
 
     Build(R"(12:34 error: cyclic dependency found: 'main' -> 'main'
 56:78 note: function 'main' calls function 'main' here)");
@@ -822,15 +824,18 @@
     // 4: fn c() { d(); }
     // 5: fn b() { c(); }
 
-    Func(Source{{1, 1}}, "a", {}, ty.void_(), {CallStmt(Call(Expr(Source{{1, 10}}, "b")))});
-    Func(Source{{2, 1}}, "e", {}, ty.void_(), {});
-    Func(Source{{3, 1}}, "d", {}, ty.void_(),
-         {
+    Func(Source{{1, 1}}, "a", utils::Empty, ty.void_(),
+         utils::Vector{CallStmt(Call(Expr(Source{{1, 10}}, "b")))});
+    Func(Source{{2, 1}}, "e", utils::Empty, ty.void_(), utils::Empty);
+    Func(Source{{3, 1}}, "d", utils::Empty, ty.void_(),
+         utils::Vector{
              CallStmt(Call(Expr(Source{{3, 10}}, "e"))),
              CallStmt(Call(Expr(Source{{3, 10}}, "b"))),
          });
-    Func(Source{{4, 1}}, "c", {}, ty.void_(), {CallStmt(Call(Expr(Source{{4, 10}}, "d")))});
-    Func(Source{{5, 1}}, "b", {}, ty.void_(), {CallStmt(Call(Expr(Source{{5, 10}}, "c")))});
+    Func(Source{{4, 1}}, "c", utils::Empty, ty.void_(),
+         utils::Vector{CallStmt(Call(Expr(Source{{4, 10}}, "d")))});
+    Func(Source{{5, 1}}, "b", utils::Empty, ty.void_(),
+         utils::Vector{CallStmt(Call(Expr(Source{{5, 10}}, "c")))});
 
     Build(R"(5:1 error: cyclic dependency found: 'b' -> 'c' -> 'd' -> 'b'
 5:10 note: function 'b' calls function 'c' here
@@ -871,7 +876,8 @@
     //   a: S;
     // };
 
-    Structure(Source{{12, 34}}, "S", {Member("a", ty.type_name(Source{{56, 78}}, "S"))});
+    Structure(Source{{12, 34}}, "S",
+              utils::Vector{Member("a", ty.type_name(Source{{56, 78}}, "S"))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -884,9 +890,9 @@
     // 2: struct X { y: Y; };
     // 3: struct Z { x: X; };
 
-    Structure(Source{{1, 1}}, "Y", {Member("z", ty.type_name(Source{{1, 10}}, "Z"))});
-    Structure(Source{{2, 1}}, "X", {Member("y", ty.type_name(Source{{2, 10}}, "Y"))});
-    Structure(Source{{3, 1}}, "Z", {Member("x", ty.type_name(Source{{3, 10}}, "X"))});
+    Structure(Source{{1, 1}}, "Y", utils::Vector{Member("z", ty.type_name(Source{{1, 10}}, "Z"))});
+    Structure(Source{{2, 1}}, "X", utils::Vector{Member("y", ty.type_name(Source{{2, 10}}, "Y"))});
+    Structure(Source{{3, 1}}, "Z", utils::Vector{Member("x", ty.type_name(Source{{3, 10}}, "X"))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -960,10 +966,10 @@
     // 5: type R = A;
     // 6: const L : S = Z;
 
-    Func(Source{{1, 1}}, "F", {}, ty.type_name(Source{{1, 5}}, "R"),
-         {Return(Expr(Source{{1, 10}}, "Z"))});
+    Func(Source{{1, 1}}, "F", utils::Empty, ty.type_name(Source{{1, 5}}, "R"),
+         utils::Vector{Return(Expr(Source{{1, 10}}, "Z"))});
     Alias(Source{{2, 1}}, "A", ty.type_name(Source{{2, 10}}, "S"));
-    Structure(Source{{3, 1}}, "S", {Member("a", ty.type_name(Source{{3, 10}}, "A"))});
+    Structure(Source{{3, 1}}, "S", utils::Vector{Member("a", ty.type_name(Source{{3, 10}}, "A"))});
     GlobalVar(Source{{4, 1}}, "Z", nullptr, Expr(Source{{4, 10}}, "L"));
     Alias(Source{{5, 1}}, "R", ty.type_name(Source{{5, 10}}, "A"));
     GlobalConst(Source{{6, 1}}, "L", ty.type_name(Source{{5, 5}}, "S"), Expr(Source{{5, 10}}, "Z"));
@@ -1039,7 +1045,7 @@
     helper.Add(use_kind, symbol, Source{{56, 78}});
     helper.Build();
 
-    ASSERT_EQ(AST().GlobalDeclarations().size(), 2u);
+    ASSERT_EQ(AST().GlobalDeclarations().Length(), 2u);
 
     auto* decl = AST().GlobalDeclarations()[0];
     auto* use = AST().GlobalDeclarations()[1];
@@ -1059,7 +1065,7 @@
     helper.Add(decl_kind, symbol, Source{{12, 34}});
     helper.Build();
 
-    ASSERT_EQ(AST().GlobalDeclarations().size(), 2u);
+    ASSERT_EQ(AST().GlobalDeclarations().Length(), 2u);
 
     auto* use = AST().GlobalDeclarations()[0];
     auto* decl = AST().GlobalDeclarations()[1];
@@ -1164,9 +1170,9 @@
     SymbolTestHelper helper(this);
     auto* outer = helper.Add(outer_kind, symbol, Source{{12, 34}});
     helper.Add(inner_kind, symbol, Source{{56, 78}});
-    auto* inner_var = helper.nested_statements.size()
+    auto* inner_var = helper.nested_statements.Length()
                           ? helper.nested_statements[0]->As<ast::VariableDeclStatement>()->variable
-                      : helper.statements.size()
+                      : helper.statements.Length()
                           ? helper.statements[0]->As<ast::VariableDeclStatement>()->variable
                           : helper.parameters[0];
     helper.Build();
@@ -1203,7 +1209,7 @@
 
     const auto* value_decl = GlobalVar(value_sym, ty.i32(), ast::StorageClass::kPrivate);
     const auto* type_decl = Alias(type_sym, ty.i32());
-    const auto* func_decl = Func(func_sym, {}, ty.void_(), {});
+    const auto* func_decl = Func(func_sym, utils::Empty, ty.void_(), utils::Empty);
 
     struct SymbolUse {
         const ast::Node* decl = nullptr;
@@ -1211,10 +1217,10 @@
         std::string where;
     };
 
-    std::vector<SymbolUse> symbol_uses;
+    utils::Vector<SymbolUse, 64> symbol_uses;
 
     auto add_use = [&](const ast::Node* decl, auto* use, int line, const char* kind) {
-        symbol_uses.emplace_back(
+        symbol_uses.Push(
             SymbolUse{decl, use, std::string(__FILE__) + ":" + std::to_string(line) + ": " + kind});
         return use;
     };
@@ -1223,13 +1229,13 @@
 #define F add_use(func_decl, Expr(func_sym), __LINE__, "F()")
 
     Alias(Sym(), T);
-    Structure(Sym(), {Member(Sym(), T)});
+    Structure(Sym(), utils::Vector{Member(Sym(), T)});
     GlobalVar(Sym(), T, V);
     GlobalConst(Sym(), T, V);
-    Func(Sym(),              //
-         {Param(Sym(), T)},  //
-         T,                  // Return type
-         {
+    Func(Sym(),                           //
+         utils::Vector{Param(Sym(), T)},  //
+         T,                               // Return type
+         utils::Vector{
              Decl(Var(Sym(), T, V)),                    //
              Decl(Let(Sym(), T, V)),                    //
              CallStmt(Call(F, V)),                      //
@@ -1278,7 +1284,7 @@
     GlobalVar(Sym(), ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
                                         ast::Access::kRead));  //
     GlobalVar(Sym(), ty.sampler(ast::SamplerKind::kSampler));
-    Func(Sym(), {}, ty.void_(), {});
+    Func(Sym(), utils::Empty, ty.void_(), utils::Empty);
 #undef V
 #undef T
 #undef F
@@ -1303,10 +1309,10 @@
 // DependencyAnalysis::SortGlobals(), found by clusterfuzz.
 // See: crbug.com/chromium/1273451
 TEST_F(ResolverDependencyGraphTraversalTest, chromium_1273451) {
-    Structure("A", {Member("a", ty.i32())});
-    Structure("B", {Member("b", ty.i32())});
-    Func("f", {Param("a", ty.type_name("A"))}, ty.type_name("B"),
-         {
+    Structure("A", utils::Vector{Member("a", ty.i32())});
+    Structure("B", utils::Vector{Member("b", ty.i32())});
+    Func("f", utils::Vector{Param("a", ty.type_name("A"))}, ty.type_name("B"),
+         utils::Vector{
              Return(Construct(ty.type_name("B"))),
          });
     Build();
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 5437828..38ebc81 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -49,8 +49,16 @@
 TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Location) {
     // @fragment
     // fn main() -> @location(0) f32 { return 1.0; }
-    Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(1_f)},
-         {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.f32(),
+         utils::Vector{
+             Return(1_f),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -58,8 +66,16 @@
 TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Builtin) {
     // @vertex
     // fn main() -> @builtin(position) vec4<f32> { return vec4<f32>(); }
-    Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::BuiltinValue::kPosition)});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
+             Return(Construct(ty.vec4<f32>())),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         },
+         utils::Vector{
+             Builtin(ast::BuiltinValue::kPosition),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -69,8 +85,13 @@
     // fn main() -> f32 {
     //   return 1.0;
     // }
-    Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kVertex)});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
+             Return(Construct(ty.vec4<f32>())),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing entry point IO attribute on return type");
@@ -81,9 +102,17 @@
     // fn main() -> @location(0) @builtin(position) vec4<f32> {
     //   return vec4<f32>();
     // }
-    Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kVertex)},
-         {Location(Source{{13, 43}}, 0), Builtin(Source{{14, 52}}, ast::BuiltinValue::kPosition)});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
+             Return(Construct(ty.vec4<f32>())),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         },
+         utils::Vector{
+             Location(Source{{13, 43}}, 0),
+             Builtin(Source{{14, 52}}, ast::BuiltinValue::kPosition),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
@@ -99,11 +128,18 @@
     // fn main() -> Output {
     //   return Output();
     // }
-    auto* output =
-        Structure("Output", {Member("a", ty.f32(), {Location(0)}),
-                             Member("b", ty.f32(), {Builtin(ast::BuiltinValue::kFragDepth)})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output = Structure(
+        "Output", utils::Vector{
+                      Member("a", ty.f32(), utils::Vector{Location(0)}),
+                      Member("b", ty.f32(), utils::Vector{Builtin(ast::BuiltinValue::kFragDepth)}),
+                  });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -116,12 +152,20 @@
     // fn main() -> Output {
     //   return Output();
     // }
-    auto* output =
-        Structure("Output", {Member("a", ty.f32(),
-                                    {Location(Source{{13, 43}}, 0),
-                                     Builtin(Source{{14, 52}}, ast::BuiltinValue::kFragDepth)})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output = Structure(
+        "Output",
+        utils::Vector{
+            Member("a", ty.f32(),
+                   utils::Vector{Location(Source{{13, 43}}, 0),
+                                 Builtin(Source{{14, 52}}, ast::BuiltinValue::kFragDepth)}),
+        });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
@@ -138,10 +182,18 @@
     // fn main() -> Output {
     //   return Output();
     // }
-    auto* output = Structure("Output", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
-                                        Member(Source{{14, 52}}, "b", ty.f32(), {})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output =
+        Structure("Output", utils::Vector{
+                                Member(Source{{13, 43}}, "a", ty.f32(), utils::Vector{Location(0)}),
+                                Member(Source{{14, 52}}, "b", ty.f32(), {}),
+                            });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -158,11 +210,18 @@
     // fn main() -> Output {
     //   return Output();
     // }
-    auto* output =
-        Structure("Output", {Member("a", ty.f32(), {Builtin(ast::BuiltinValue::kFragDepth)}),
-                             Member("b", ty.f32(), {Builtin(ast::BuiltinValue::kFragDepth)})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output = Structure(
+        "Output", utils::Vector{
+                      Member("a", ty.f32(), utils::Vector{Builtin(ast::BuiltinValue::kFragDepth)}),
+                      Member("b", ty.f32(), utils::Vector{Builtin(ast::BuiltinValue::kFragDepth)}),
+                  });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -174,8 +233,18 @@
 TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Location) {
     // @fragment
     // fn main(@location(0) param : f32) {}
-    auto* param = Param("param", ty.f32(), {Location(0)});
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    auto* param = Param("param", ty.f32(),
+                        utils::Vector{
+                            Location(0),
+                        });
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -184,7 +253,14 @@
     // @fragment
     // fn main(param : f32) {}
     auto* param = Param(Source{{13, 43}}, "param", ty.vec4<f32>());
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "13:43 error: missing entry point IO attribute on parameter");
@@ -194,9 +270,18 @@
     // @fragment
     // fn main(@location(0) @builtin(sample_index) param : u32) {}
     auto* param = Param("param", ty.u32(),
-                        {Location(Source{{13, 43}}, 0),
-                         Builtin(Source{{14, 52}}, ast::BuiltinValue::kSampleIndex)});
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+                        utils::Vector{
+                            Location(Source{{13, 43}}, 0),
+                            Builtin(Source{{14, 52}}, ast::BuiltinValue::kSampleIndex),
+                        });
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
@@ -210,11 +295,20 @@
     // };
     // @fragment
     // fn main(param : Input) {}
-    auto* input =
-        Structure("Input", {Member("a", ty.f32(), {Location(0)}),
-                            Member("b", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)})});
+    auto* input = Structure(
+        "Input", utils::Vector{
+                     Member("a", ty.f32(), utils::Vector{Location(0)}),
+                     Member("b", ty.u32(), utils::Vector{Builtin(ast::BuiltinValue::kSampleIndex)}),
+                 });
     auto* param = Param("param", ty.Of(input));
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -225,12 +319,22 @@
     // };
     // @fragment
     // fn main(param : Input) {}
-    auto* input =
-        Structure("Input", {Member("a", ty.u32(),
-                                   {Location(Source{{13, 43}}, 0),
-                                    Builtin(Source{{14, 52}}, ast::BuiltinValue::kSampleIndex)})});
+    auto* input = Structure(
+        "Input",
+        utils::Vector{
+            Member("a", ty.u32(),
+                   utils::Vector{Location(Source{{13, 43}}, 0),
+                                 Builtin(Source{{14, 52}}, ast::BuiltinValue::kSampleIndex)}),
+        });
     auto* param = Param("param", ty.Of(input));
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
@@ -245,10 +349,20 @@
     // };
     // @fragment
     // fn main(param : Input) {}
-    auto* input = Structure("Input", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
-                                      Member(Source{{14, 52}}, "b", ty.f32(), {})});
+    auto* input =
+        Structure("Input", utils::Vector{
+                               Member(Source{{13, 43}}, "a", ty.f32(), utils::Vector{Location(0)}),
+                               Member(Source{{14, 52}}, "b", ty.f32(), {}),
+                           });
     auto* param = Param("param", ty.Of(input));
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(14:52 error: missing entry point IO attribute
@@ -259,10 +373,23 @@
     // @fragment
     // fn main(@builtin(sample_index) param_a : u32,
     //         @builtin(sample_index) param_b : u32) {}
-    auto* param_a = Param("param_a", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)});
-    auto* param_b = Param("param_b", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)});
-    Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* param_a = Param("param_a", ty.u32(),
+                          utils::Vector{
+                              Builtin(ast::BuiltinValue::kSampleIndex),
+                          });
+    auto* param_b = Param("param_b", ty.u32(),
+                          utils::Vector{
+                              Builtin(ast::BuiltinValue::kSampleIndex),
+                          });
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param_a,
+             param_b,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -279,14 +406,27 @@
     // };
     // @fragment
     // fn main(param_a : InputA, param_b : InputB) {}
-    auto* input_a =
-        Structure("InputA", {Member("a", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)})});
-    auto* input_b =
-        Structure("InputB", {Member("a", ty.u32(), {Builtin(ast::BuiltinValue::kSampleIndex)})});
+    auto* input_a = Structure(
+        "InputA",
+        utils::Vector{
+            Member("a", ty.u32(), utils::Vector{Builtin(ast::BuiltinValue::kSampleIndex)}),
+        });
+    auto* input_b = Structure(
+        "InputB",
+        utils::Vector{
+            Member("a", ty.u32(), utils::Vector{Builtin(ast::BuiltinValue::kSampleIndex)}),
+        });
     auto* param_a = Param("param_a", ty.Of(input_a));
     auto* param_b = Param("param_b", ty.Of(input_b));
-    Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param_a,
+             param_b,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -298,7 +438,10 @@
 TEST_F(ResolverEntryPointValidationTest, VertexShaderMustReturnPosition) {
     // @vertex
     // fn main() {}
-    Func(Source{{12, 34}}, "main", {}, ty.void_(), {}, {Stage(ast::PipelineStage::kVertex)});
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -477,8 +620,19 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* a = Param("a", params.create_ast_type(*this), {Location(0), Flat()});
-    Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    auto* a = Param("a", params.create_ast_type(*this),
+                    utils::Vector{
+                        Location(0),
+                        Flat(),
+                    });
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             a,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -497,10 +651,19 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* input =
-        Structure("Input", {Member("a", params.create_ast_type(*this), {Location(0), Flat()})});
+    auto* input = Structure(
+        "Input", utils::Vector{
+                     Member("a", params.create_ast_type(*this), utils::Vector{Location(0), Flat()}),
+                 });
     auto* a = Param("a", ty.Of(input), {});
-    Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             a,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -518,9 +681,16 @@
 
     Enable(ast::Extension::kF16);
 
-    Func(Source{{12, 34}}, "main", {}, params.create_ast_type(*this),
-         {Return(Construct(params.create_ast_type(*this)))}, {Stage(ast::PipelineStage::kFragment)},
-         {Location(0)});
+    Func(Source{{12, 34}}, "main", utils::Empty, params.create_ast_type(*this),
+         utils::Vector{
+             Return(Construct(params.create_ast_type(*this))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(0),
+         });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -541,9 +711,17 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* output = Structure("Output", {Member("a", params.create_ast_type(*this), {Location(0)})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output = Structure(
+        "Output", utils::Vector{
+                      Member("a", params.create_ast_type(*this), utils::Vector{Location(0)}),
+                  });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -565,8 +743,19 @@
     // @fragment
     // fn frag_main(@location(0) @interpolate(flat) a: i32) {}
 
-    auto* p = Param(Source{{12, 34}}, "a", ty.i32(), {Location(0), Flat()});
-    Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    auto* p = Param(Source{{12, 34}}, "a", ty.i32(),
+                    utils::Vector{
+                        Location(0),
+                        Flat(),
+                    });
+    Func("frag_main",
+         utils::Vector{
+             p,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -575,8 +764,18 @@
     // @fragment
     // fn frag_main(@location(0) a: bool) {}
 
-    auto* p = Param(Source{{12, 34}}, "a", ty.bool_(), {Location(Source{{34, 56}}, 0)});
-    Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    auto* p = Param(Source{{12, 34}}, "a", ty.bool_(),
+                    utils::Vector{
+                        Location(Source{{34, 56}}, 0),
+                    });
+    Func("frag_main",
+         utils::Vector{
+             p,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -590,9 +789,16 @@
     // @fragment
     // fn frag_main()->@location(0) array<f32, 2> { return array<f32, 2>(); }
 
-    Func(Source{{12, 34}}, "frag_main", {}, ty.array<f32, 2>(),
-         {Return(Construct(ty.array<f32, 2>()))}, {Stage(ast::PipelineStage::kFragment)},
-         {Location(Source{{34, 56}}, 0)});
+    Func(Source{{12, 34}}, "frag_main", utils::Empty, ty.array<f32, 2>(),
+         utils::Vector{
+             Return(Construct(ty.array<f32, 2>())),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         utils::Vector{
+             Location(Source{{34, 56}}, 0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -608,9 +814,21 @@
     // };
     // @fragment
     // fn main(@location(0) param : Input) {}
-    auto* input = Structure("Input", {Member("a", ty.f32())});
-    auto* param = Param(Source{{12, 34}}, "param", ty.Of(input), {Location(Source{{13, 43}}, 0)});
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    auto* input = Structure("Input", utils::Vector{
+                                         Member("a", ty.f32()),
+                                     });
+    auto* param = Param(Source{{12, 34}}, "param", ty.Of(input),
+                        utils::Vector{
+                            Location(Source{{13, 43}}, 0),
+                        });
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -629,10 +847,22 @@
     // };
     // @fragment
     // fn main(param : Input) {}
-    auto* inner = Structure("Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
-    auto* input = Structure("Input", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
+    auto* inner =
+        Structure("Inner", utils::Vector{
+                               Member(Source{{13, 43}}, "a", ty.f32(), utils::Vector{Location(0)}),
+                           });
+    auto* input = Structure("Input", utils::Vector{
+                                         Member(Source{{14, 52}}, "a", ty.Of(inner)),
+                                     });
     auto* param = Param("param", ty.Of(input));
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -646,10 +876,19 @@
     // };
     // @fragment
     // fn main(param : Input) {}
-    auto* input =
-        Structure("Input", {Member(Source{{13, 43}}, "a", ty.array<f32>(), {Location(0)})});
+    auto* input = Structure(
+        "Input", utils::Vector{
+                     Member(Source{{13, 43}}, "a", ty.array<f32>(), utils::Vector{Location(0)}),
+                 });
     auto* param = Param("param", ty.Of(input));
-    Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -665,11 +904,20 @@
     // fn frag_main( a: S) {}
 
     auto* m = Member(Source{{34, 56}}, "m", ty.array<i32>(),
-                     ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-    auto* s = Structure("S", {m});
+                     utils::Vector{
+                         Location(Source{{12, 34}}, 0u),
+                     });
+    auto* s = Structure("S", utils::Vector{m});
     auto* p = Param("a", ty.Of(s));
 
-    Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+    Func("frag_main",
+         utils::Vector{
+             p,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -684,11 +932,19 @@
     // @fragment
     // fn frag_main() -> S {}
     auto* m = Member(Source{{34, 56}}, "m", ty.atomic<i32>(),
-                     ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-    auto* s = Structure("S", {m});
+                     utils::Vector{
+                         Location(Source{{12, 34}}, 0u),
+                     });
+    auto* s = Structure("S", utils::Vector{m});
 
-    Func("frag_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
-         {Stage(ast::PipelineStage::kFragment)}, {});
+    Func("frag_main", utils::Empty, ty.Of(s),
+         utils::Vector{
+             Return(Construct(ty.Of(s))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         },
+         {});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -702,8 +958,10 @@
     // struct S { @location(0) m: mat3x2<f32>; };
 
     auto* m = Member(Source{{34, 56}}, "m", ty.mat3x2<f32>(),
-                     ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-    Structure("S", {m});
+                     utils::Vector{
+                         Location(Source{{12, 34}}, 0u),
+                     });
+    Structure("S", utils::Vector{m});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -722,11 +980,18 @@
     // fn main() -> Output {
     //   return Output();
     // }
-    auto* output =
-        Structure("Output", {Member("a", ty.f32(), {Location(0)}),
-                             Member("b", ty.f32(), {Builtin(ast::BuiltinValue::kFragDepth)})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output = Structure(
+        "Output", utils::Vector{
+                      Member("a", ty.f32(), utils::Vector{Location(0)}),
+                      Member("b", ty.f32(), utils::Vector{Builtin(ast::BuiltinValue::kFragDepth)}),
+                  });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -739,9 +1004,19 @@
     // fn main() -> @location(0) Output {
     //   return Output();
     // }
-    auto* output = Structure("Output", {Member("a", ty.f32())});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kVertex)}, {Location(Source{{13, 43}}, 0)});
+    auto* output = Structure("Output", utils::Vector{
+                                           Member("a", ty.f32()),
+                                       });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kVertex),
+         },
+         utils::Vector{
+             Location(Source{{13, 43}}, 0),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -760,10 +1035,20 @@
     // };
     // @fragment
     // fn main() -> Output { return Output(); }
-    auto* inner = Structure("Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
-    auto* output = Structure("Output", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* inner =
+        Structure("Inner", utils::Vector{
+                               Member(Source{{13, 43}}, "a", ty.f32(), utils::Vector{Location(0)}),
+                           });
+    auto* output = Structure("Output", utils::Vector{
+                                           Member(Source{{14, 52}}, "a", ty.Of(inner)),
+                                       });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -779,10 +1064,17 @@
     // fn main() -> Output {
     //   return Output();
     // }
-    auto* output = Structure("Output", {Member(Source{{13, 43}}, "a", ty.array<f32>(),
-                                               {Location(Source{{12, 34}}, 0)})});
-    Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* output = Structure("Output", utils::Vector{
+                                           Member(Source{{13, 43}}, "a", ty.array<f32>(),
+                                                  utils::Vector{Location(Source{{12, 34}}, 0)}),
+                                       });
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.Of(output),
+         utils::Vector{
+             Return(Construct(ty.Of(output))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -793,32 +1085,51 @@
 }
 
 TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
-    Func("main", {}, ty.i32(), {Return(Expr(1_i))},
-         {Stage(ast::PipelineStage::kCompute),
-          create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))},
-         ast::AttributeList{Location(Source{{12, 34}}, 1)});
+    Func("main", utils::Empty, ty.i32(),
+         utils::Vector{
+             Return(Expr(1_i)),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         },
+         utils::Vector{
+             Location(Source{{12, 34}}, 1),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader output");
 }
 
 TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
-    auto* input = Param("input", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-    Func("main", {input}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))});
+    auto* input = Param("input", ty.i32(),
+                        utils::Vector{
+                            Location(Source{{12, 34}}, 0u),
+                        });
+    Func("main", utils::Vector{input}, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader inputs");
 }
 
 TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
-    auto* m = Member("m", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-    auto* s = Structure("S", {m});
-    Func(Source{{56, 78}}, "main", {}, ty.Of(s),
-         ast::StatementList{Return(Expr(Construct(ty.Of(s))))},
-         {Stage(ast::PipelineStage::kCompute),
-          create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))});
+    auto* m = Member("m", ty.i32(),
+                     utils::Vector{
+                         Location(Source{{12, 34}}, 0u),
+                     });
+    auto* s = Structure("S", utils::Vector{m});
+    Func(Source{{56, 78}}, "main", utils::Empty, ty.Of(s),
+         utils::Vector{
+             Return(Expr(Construct(ty.Of(s)))),
+         },
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -827,12 +1138,17 @@
 }
 
 TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Input) {
-    auto* m = Member("m", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
-    auto* s = Structure("S", {m});
+    auto* m = Member("m", ty.i32(),
+                     utils::Vector{
+                         Location(Source{{12, 34}}, 0u),
+                     });
+    auto* s = Structure("S", utils::Vector{m});
     auto* input = Param("input", ty.Of(s));
-    Func(Source{{56, 78}}, "main", {input}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i))});
+    Func(Source{{56, 78}}, "main", utils::Vector{input}, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -844,10 +1160,23 @@
     // @fragment
     // fn main(@location(1) param_a : f32,
     //         @location(1) param_b : f32) {}
-    auto* param_a = Param("param_a", ty.f32(), {Location(1)});
-    auto* param_b = Param("param_b", ty.f32(), {Location(Source{{12, 34}}, 1)});
-    Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    auto* param_a = Param("param_a", ty.f32(),
+                          utils::Vector{
+                              Location(1),
+                          });
+    auto* param_b = Param("param_b", ty.f32(),
+                          utils::Vector{
+                              Location(Source{{12, 34}}, 1),
+                          });
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param_a,
+             param_b,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: location(1) attribute appears multiple times");
@@ -862,12 +1191,24 @@
     // };
     // @fragment
     // fn main(param_a : InputA, param_b : InputB) {}
-    auto* input_a = Structure("InputA", {Member("a", ty.f32(), {Location(1)})});
-    auto* input_b = Structure("InputB", {Member("a", ty.f32(), {Location(Source{{34, 56}}, 1)})});
+    auto* input_a = Structure("InputA", utils::Vector{
+                                            Member("a", ty.f32(), utils::Vector{Location(1)}),
+                                        });
+    auto* input_b =
+        Structure("InputB", utils::Vector{
+                                Member("a", ty.f32(), utils::Vector{Location(Source{{34, 56}}, 1)}),
+                            });
     auto* param_a = Param("param_a", ty.Of(input_a));
     auto* param_b = Param("param_b", ty.Of(input_b));
-    Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{12, 34}}, "main",
+         utils::Vector{
+             param_a,
+             param_b,
+         },
+         ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/evaluation_stage_test.cc b/src/tint/resolver/evaluation_stage_test.cc
index 236e4b3..4b603bb 100644
--- a/src/tint/resolver/evaluation_stage_test.cc
+++ b/src/tint/resolver/evaluation_stage_test.cc
@@ -269,7 +269,7 @@
     // struct S { m : i32 };
     // const str = S();
     // str.m
-    Structure("S", {Member("m", ty.i32())});
+    Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* str = Const("str", nullptr, Construct(ty.type_name("S")));
     auto* expr = MemberAccessor(str, "m");
     WrapInFunction(str, expr);
@@ -283,7 +283,7 @@
     // struct S { m : i32 };
     // var str = S();
     // str.m
-    Structure("S", {Member("m", ty.i32())});
+    Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* str = Var("str", nullptr, Construct(ty.type_name("S")));
     auto* expr = MemberAccessor(str, "m");
     WrapInFunction(str, expr);
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index c1ea33c..372d91a 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -30,8 +30,8 @@
 TEST_F(ResolverFunctionValidationTest, DuplicateParameterName) {
     // fn func_a(common_name : f32) { }
     // fn func_b(common_name : f32) { }
-    Func("func_a", {Param("common_name", ty.f32())}, ty.void_(), {});
-    Func("func_b", {Param("common_name", ty.f32())}, ty.void_(), {});
+    Func("func_a", utils::Vector{Param("common_name", ty.f32())}, ty.void_(), utils::Empty);
+    Func("func_b", utils::Vector{Param("common_name", ty.f32())}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -40,7 +40,7 @@
     // var<private> common_name : f32;
     // fn func(common_name : f32) { }
     GlobalVar("common_name", ty.f32(), ast::StorageClass::kPrivate);
-    Func("func", {Param("common_name", ty.f32())}, ty.void_(), {});
+    Func("func", utils::Vector{Param("common_name", ty.f32())}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -49,8 +49,10 @@
     // fn func(common_name : f32) {
     //   let common_name = 1i;
     // }
-    Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
-         {Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1_i)))});
+    Func("func", utils::Vector{Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
+         utils::Vector{
+             Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1_i))),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(56:78 error: redeclaration of 'common_name'
@@ -59,12 +61,14 @@
 
 TEST_F(ResolverFunctionValidationTest, NestedLocalMayShadowParameter) {
     // fn func(common_name : f32) {
-    //   {
+    // utils::Vector  {
     //     let common_name = 1i;
     //   }
     // }
-    Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
-         {Block(Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1_i))))});
+    Func("func", utils::Vector{Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
+         utils::Vector{
+             Block(Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1_i)))),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -73,8 +77,8 @@
     // fn func { var a:i32 = 2i; }
     auto* var = Var("a", ty.i32(), Expr(2_i));
 
-    Func(Source{{12, 34}}, "func", {}, ty.void_(),
-         {
+    Func(Source{{12, 34}}, "func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
          });
 
@@ -88,8 +92,8 @@
     // }
 
     auto* var = Var("func", ty.i32(), Expr(0_i));
-    Func("func", {}, ty.i32(),
-         {
+    Func("func", utils::Empty, ty.i32(),
+         utils::Vector{
              Decl(var),
              Return(Source{{12, 34}}, Expr("func")),
          });
@@ -102,13 +106,13 @@
     // fn b() -> i32 { return 2; }
 
     auto* var = Var("b", ty.i32(), Expr(0_i));
-    Func("a", {}, ty.void_(),
-         {
+    Func("a", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
          });
 
-    Func(Source{{12, 34}}, "b", {}, ty.i32(),
-         {
+    Func(Source{{12, 34}}, "b", utils::Empty, ty.i32(),
+         utils::Vector{
              Return(2_i),
          });
 
@@ -126,7 +130,7 @@
     auto* ret = Return();
     auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
 
-    Func("func", {}, ty.void_(), {decl_a, ret, assign_a});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{decl_a, ret, assign_a});
 
     ASSERT_TRUE(r()->Resolve());
 
@@ -139,7 +143,7 @@
 TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
     // fn func() -> {
     //  var a : i32;
-    //  {{{return;}}}
+    // utils::Vector  {{{return;}}}
     //  a = 2i;
     //}
 
@@ -147,7 +151,8 @@
     auto* ret = Return();
     auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
 
-    Func("func", {}, ty.void_(), {decl_a, Block(Block(Block(ret))), assign_a});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{decl_a, Block(Block(Block(ret))), assign_a});
 
     ASSERT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
@@ -167,7 +172,7 @@
     auto* discard = Discard();
     auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
 
-    Func("func", {}, ty.void_(), {decl_a, discard, assign_a});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{decl_a, discard, assign_a});
 
     ASSERT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
@@ -179,7 +184,7 @@
 TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
     // fn func() -> {
     //  var a : i32;
-    //  {{{discard;}}}
+    // utils::Vector  {{{discard;}}}
     //  a = 2i;
     //}
 
@@ -187,7 +192,8 @@
     auto* discard = Discard();
     auto* assign_a = Assign(Source{{12, 34}}, "a", 2_i);
 
-    Func("func", {}, ty.void_(), {decl_a, Block(Block(Block(discard))), assign_a});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{decl_a, Block(Block(Block(discard))), assign_a});
 
     ASSERT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
@@ -201,8 +207,8 @@
 
     auto* var = Var("a", ty.i32(), Expr(2_i));
 
-    Func(Source{{12, 34}}, "func", {}, ty.i32(),
-         {
+    Func(Source{{12, 34}}, "func", utils::Empty, ty.i32(),
+         utils::Vector{
              Decl(var),
          });
 
@@ -213,7 +219,7 @@
 TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
     // fn func {}
 
-    Func(Source{{12, 34}}, "func", {}, ty.void_(), {});
+    Func(Source{{12, 34}}, "func", utils::Empty, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -221,7 +227,7 @@
 TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
     // fn func() -> int {}
 
-    Func(Source{{12, 34}}, "func", {}, ty.i32(), {});
+    Func(Source{{12, 34}}, "func", utils::Empty, ty.i32(), utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
@@ -230,14 +236,20 @@
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_Pass) {
     // fn func { return; }
 
-    Func("func", {}, ty.void_(), {Return()});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Return(),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsAInt) {
     // fn func { return 2; }
-    Func("func", {}, ty.void_(), {Return(Source{{12, 34}}, Expr(2_a))});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Return(Source{{12, 34}}, Expr(2_a)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -247,7 +259,10 @@
 
 TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsAFloat) {
     // fn func { return 2.0; }
-    Func("func", {}, ty.void_(), {Return(Source{{12, 34}}, Expr(2.0_a))});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Return(Source{{12, 34}}, Expr(2.0_a)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -257,7 +272,10 @@
 
 TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsI32) {
     // fn func { return 2i; }
-    Func("func", {}, ty.void_(), {Return(Source{{12, 34}}, Expr(2_i))});
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
+             Return(Source{{12, 34}}, Expr(2_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -268,9 +286,12 @@
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_void_fail) {
     // fn v { return; }
     // fn func { return v(); }
-    Func("v", {}, ty.void_(), {Return()});
-    Func("func", {}, ty.void_(),
-         {
+    Func("v", utils::Empty, ty.void_(),
+         utils::Vector{
+             Return(),
+         });
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
              Return(Call(Source{{12, 34}}, "v")),
          });
 
@@ -280,8 +301,8 @@
 
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeMissing_fail) {
     // fn func() -> f32 { return; }
-    Func("func", {}, ty.f32(),
-         {
+    Func("func", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(Source{{12, 34}}, nullptr),
          });
 
@@ -293,8 +314,8 @@
 
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_pass) {
     // fn func() -> f32 { return 2.0; }
-    Func("func", {}, ty.f32(),
-         {
+    Func("func", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(Source{{12, 34}}, Expr(2_f)),
          });
 
@@ -303,8 +324,8 @@
 
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
     // fn func() -> f32 { return 2i; }
-    Func("func", {}, ty.f32(),
-         {
+    Func("func", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(Source{{12, 34}}, Expr(2_i)),
          });
 
@@ -318,8 +339,8 @@
     // type myf32 = f32;
     // fn func() -> myf32 { return 2.0; }
     auto* myf32 = Alias("myf32", ty.f32());
-    Func("func", {}, ty.Of(myf32),
-         {
+    Func("func", utils::Empty, ty.Of(myf32),
+         utils::Vector{
              Return(Source{{12, 34}}, Expr(2_f)),
          });
 
@@ -330,8 +351,8 @@
     // type myf32 = f32;
     // fn func() -> myf32 { return 2u; }
     auto* myf32 = Alias("myf32", ty.f32());
-    Func("func", {}, ty.Of(myf32),
-         {
+    Func("func", utils::Empty, ty.Of(myf32),
+         utils::Vector{
              Return(Source{{12, 34}}, Expr(2_u)),
          });
 
@@ -344,11 +365,14 @@
 TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
     // @compute @workgroup_size(1) fn entrypoint() {}
     // fn func() { return entrypoint(); }
-    Func("entrypoint", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    Func("entrypoint", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
 
-    Func("func", {}, ty.void_(),
-         {
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
              CallStmt(Call(Source{{12, 34}}, "entrypoint")),
          });
 
@@ -360,7 +384,10 @@
 TEST_F(ResolverFunctionValidationTest, CannotCallFunctionAtModuleScope) {
     // fn F() -> i32 { return 1; }
     // var x = F();
-    Func("F", {}, ty.i32(), {Return(1_i)});
+    Func("F", utils::Empty, ty.i32(),
+         utils::Vector{
+             Return(1_i),
+         });
     GlobalVar("x", nullptr, Call(Source{{12, 34}}, "F"), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -371,11 +398,11 @@
     // @fragment
     // @vertex
     // fn main() { return; }
-    Func(Source{{12, 34}}, "main", {}, ty.void_(),
-         {
+    Func(Source{{12, 34}}, "main", utils::Empty, ty.void_(),
+         utils::Vector{
              Return(),
          },
-         ast::AttributeList{
+         utils::Vector{
              Stage(Source{{12, 34}}, ast::PipelineStage::kVertex),
              Stage(Source{{56, 78}}, ast::PipelineStage::kFragment),
          });
@@ -387,8 +414,8 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, NoPipelineEntryPoints) {
-    Func("vtx_func", {}, ty.void_(),
-         {
+    Func("vtx_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Return(),
          });
 
@@ -403,8 +430,8 @@
     auto* bar = Param("bar", ty.f32());
     auto* baz = Var("baz", ty.f32(), Expr("bar"));
 
-    Func("foo", {bar}, ty.void_(),
-         {
+    Func("foo", utils::Vector{bar}, ty.void_(),
+         utils::Vector{
              Decl(baz),
          });
 
@@ -419,8 +446,8 @@
     auto* bar = Param("bar", ty.f32());
     auto* baz = Let("baz", ty.f32(), Expr("bar"));
 
-    Func("foo", {bar}, ty.void_(),
-         {
+    Func("foo", utils::Vector{bar}, ty.void_(),
+         utils::Vector{
              Decl(baz),
          });
 
@@ -428,8 +455,11 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, FunctionParamsConst) {
-    Func("foo", {Param(Sym("arg"), ty.i32())}, ty.void_(),
-         {Assign(Expr(Source{{12, 34}}, "arg"), Expr(1_i)), Return()});
+    Func("foo", utils::Vector{Param(Sym("arg"), ty.i32())}, ty.void_(),
+         utils::Vector{
+             Assign(Expr(Source{{12, 34}}, "arg"), Expr(1_i)),
+             Return(),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -443,8 +473,11 @@
     // fn main() {}
     auto* x = GlobalConst("x", ty.u32(), Expr(4_u));
     auto* y = GlobalConst("y", ty.u32(), Expr(8_u));
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {Stage(ast::PipelineStage::kCompute), WorkgroupSize("x", "y", 16_u)});
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
+                          Stage(ast::PipelineStage::kCompute),
+                          WorkgroupSize("x", "y", 16_u),
+                      });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -464,8 +497,11 @@
     // @compute @workgroup_size(1i, 2i, 3i)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_i, 2_i, 3_i)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_i, 2_i, 3_i),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -474,8 +510,11 @@
     // @compute @workgroup_size(1u, 2u, 3u)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_u, 2_u, 3_u)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_u, 2_u, 3_u),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -484,8 +523,11 @@
     // @compute @workgroup_size(1, 2i, 3)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_a, 2_i, 3_a)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_a, 2_i, 3_a),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -494,8 +536,11 @@
     // @compute @workgroup_size(1u, 2, 3u)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_u, 2_a, 3_u)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_u, 2_a, 3_u),
+         });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -504,8 +549,11 @@
     // @compute @workgroup_size(1u, 2, 3_i)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_u, 2_a, 3_i)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_u, 2_a, 3_i),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -516,8 +564,11 @@
     // @compute @workgroup_size(1_i, 2u, 3)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_i, 2_u, 3_a)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_i, 2_u, 3_a),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -529,8 +580,11 @@
     // @compute @workgroup_size(1i, x)
     // fn main() {}
     GlobalConst("x", ty.u32(), Expr(64_u));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, 1_i, "x")});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, 1_i, "x"),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -544,8 +598,11 @@
     // fn main() {}
     GlobalConst("x", ty.u32(), Expr(64_u));
     GlobalConst("y", ty.i32(), Expr(32_i));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, "x", "y")});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, "x", "y"),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -559,8 +616,11 @@
     // fn main() {}
     GlobalConst("x", ty.u32(), Expr(4_u));
     GlobalConst("y", ty.u32(), Expr(8_u));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Source{{12, 34}}, "x", "y", 16_i)});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Source{{12, 34}}, "x", "y", 16_i),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -571,8 +631,11 @@
     // @compute @workgroup_size(64.0)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, 64_f))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, 64_f)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -584,8 +647,11 @@
     // @compute @workgroup_size(-2i)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, -2_i))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, -2_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
@@ -595,8 +661,11 @@
     // @compute @workgroup_size(0i)
     // fn main() {}
 
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, 0_i))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, 0_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
@@ -607,8 +676,11 @@
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.f32(), Expr(64_f));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, "x")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -621,8 +693,11 @@
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.i32(), Expr(-2_i));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, "x")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
@@ -633,8 +708,11 @@
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.i32(), Expr(0_i));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, "x")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
@@ -645,8 +723,11 @@
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.i32(), Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32()))));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, "x")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
@@ -657,8 +738,11 @@
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalVar("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64_i));
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Expr(Source{{12, 34}}, "x")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -669,9 +753,11 @@
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_InvalidExpr) {
     // @compute @workgroup_size(i32(1))
     // fn main() {}
-    Func("main", {}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute),
-          WorkgroupSize(Construct(Source{{12, 34}}, ty.i32(), 1_i))});
+    Func("main", utils::Empty, ty.void_(), utils::Empty,
+         utils::Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(Construct(Source{{12, 34}}, ty.i32(), 1_i)),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -681,7 +767,7 @@
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_NonPlain) {
     auto* ret_type = ty.pointer(Source{{12, 34}}, ty.i32(), ast::StorageClass::kFunction);
-    Func("f", {}, ret_type, {});
+    Func("f", utils::Empty, ret_type, utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
@@ -689,7 +775,7 @@
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_AtomicInt) {
     auto* ret_type = ty.atomic(Source{{12, 34}}, ty.i32());
-    Func("f", {}, ret_type, {});
+    Func("f", utils::Empty, ret_type, utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
@@ -697,16 +783,18 @@
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_ArrayOfAtomic) {
     auto* ret_type = ty.array(Source{{12, 34}}, ty.atomic(ty.i32()), 10_u);
-    Func("f", {}, ret_type, {});
+    Func("f", utils::Empty, ret_type, utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
 }
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_StructOfAtomic) {
-    Structure("S", {Member("m", ty.atomic(ty.i32()))});
+    Structure("S", utils::Vector{
+                       Member("m", ty.atomic(ty.i32())),
+                   });
     auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
-    Func("f", {}, ret_type, {});
+    Func("f", utils::Empty, ret_type, utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
@@ -714,47 +802,51 @@
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_RuntimeArray) {
     auto* ret_type = ty.array(Source{{12, 34}}, ty.i32());
-    Func("f", {}, ret_type, {});
+    Func("f", utils::Empty, ret_type, utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
 }
 
 TEST_F(ResolverFunctionValidationTest, ParameterStoreType_NonAtomicFree) {
-    Structure("S", {Member("m", ty.atomic(ty.i32()))});
+    Structure("S", utils::Vector{
+                       Member("m", ty.atomic(ty.i32())),
+                   });
     auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
     auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
-    Func("f", {bar}, ty.void_(), {});
+    Func("f", utils::Vector{bar}, ty.void_(), utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: type of function parameter must be constructible");
 }
 
 TEST_F(ResolverFunctionValidationTest, ParameterSotreType_AtomicFree) {
-    Structure("S", {Member("m", ty.i32())});
+    Structure("S", utils::Vector{
+                       Member("m", ty.i32()),
+                   });
     auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
     auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
-    Func("f", {bar}, ty.void_(), {});
+    Func("f", utils::Vector{bar}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverFunctionValidationTest, ParametersAtLimit) {
-    ast::ParameterList params;
+    utils::Vector<const ast::Parameter*, 256> params;
     for (int i = 0; i < 255; i++) {
-        params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
+        params.Push(Param("param_" + std::to_string(i), ty.i32()));
     }
-    Func(Source{{12, 34}}, "f", params, ty.void_(), {});
+    Func(Source{{12, 34}}, "f", params, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverFunctionValidationTest, ParametersOverLimit) {
-    ast::ParameterList params;
+    utils::Vector<const ast::Parameter*, 256> params;
     for (int i = 0; i < 256; i++) {
-        params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
+        params.Push(Param("param_" + std::to_string(i), ty.i32()));
     }
-    Func(Source{{12, 34}}, "f", params, ty.void_(), {});
+    Func(Source{{12, 34}}, "f", params, ty.void_(), utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: functions may declare at most 255 parameters");
@@ -763,8 +855,9 @@
 TEST_F(ResolverFunctionValidationTest, ParameterVectorNoType) {
     // fn f(p : vec3) {}
 
-    Func(Source{{12, 34}}, "f", {Param("p", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u))},
-         ty.void_(), {});
+    Func(Source{{12, 34}}, "f",
+         utils::Vector{Param("p", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u))}, ty.void_(),
+         utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
@@ -774,7 +867,8 @@
     // fn f(p : vec3) {}
 
     Func(Source{{12, 34}}, "f",
-         {Param("p", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u))}, ty.void_(), {});
+         utils::Vector{Param("p", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u))},
+         ty.void_(), utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
@@ -792,7 +886,7 @@
     auto& param = GetParam();
     auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.storage_class);
     auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
-    Func("f", {arg}, ty.void_(), {});
+    Func("f", utils::Vector{arg}, ty.void_(), utils::Empty);
 
     if (param.should_pass) {
         ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index 1bd9ed7..d67a108 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -24,10 +24,10 @@
 using ResolverHostShareableValidationTest = ResolverTest;
 
 TEST_F(ResolverHostShareableValidationTest, BoolMember) {
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.bool_())});
 
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -42,10 +42,10 @@
 }
 
 TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
 
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -61,10 +61,10 @@
 
 TEST_F(ResolverHostShareableValidationTest, Aliases) {
     auto* a1 = Alias("a1", ty.bool_());
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.Of(a1))});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.Of(a1))});
     auto* a2 = Alias("a2", ty.Of(s));
     GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -79,14 +79,14 @@
 }
 
 TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
-    auto* i1 = Structure("I1", {Member(Source{{1, 2}}, "x", ty.bool_())});
-    auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", ty.Of(i1))});
-    auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", ty.Of(i2))});
+    auto* i1 = Structure("I1", utils::Vector{Member(Source{{1, 2}}, "x", ty.bool_())});
+    auto* i2 = Structure("I2", utils::Vector{Member(Source{{3, 4}}, "y", ty.Of(i1))});
+    auto* i3 = Structure("I3", utils::Vector{Member(Source{{5, 6}}, "z", ty.Of(i2))});
 
-    auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
+    auto* s = Structure("S", utils::Vector{Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
     GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -106,33 +106,32 @@
 TEST_F(ResolverHostShareableValidationTest, NoError) {
     Enable(ast::Extension::kF16);
 
-    auto* i1 = Structure("I1", {
+    auto* i1 = Structure("I1", utils::Vector{
                                    Member(Source{{1, 1}}, "w1", ty.f32()),
                                    Member(Source{{2, 1}}, "x1", ty.f32()),
                                    Member(Source{{3, 1}}, "y1", ty.vec3<f32>()),
                                    Member(Source{{4, 1}}, "z1", ty.array<i32, 4>()),
                                });
     auto* a1 = Alias("a1", ty.Of(i1));
-    auto* i2 = Structure("I2", {
+    auto* i2 = Structure("I2", utils::Vector{
                                    Member(Source{{5, 1}}, "x2", ty.mat2x2<f32>()),
                                    Member(Source{{6, 1}}, "w2", ty.mat3x4<f32>()),
                                    Member(Source{{7, 1}}, "z2", ty.Of(i1)),
                                });
     auto* a2 = Alias("a2", ty.Of(i2));
-    auto* i3 = Structure("I3", {
+    auto* i3 = Structure("I3", utils::Vector{
                                    Member(Source{{4, 1}}, "x3", ty.Of(a1)),
                                    Member(Source{{5, 1}}, "y3", ty.Of(i2)),
                                    Member(Source{{6, 1}}, "z3", ty.Of(a2)),
                                });
 
-    auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
+    auto* s = Structure("S", utils::Vector{Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
     GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
-    WrapInFunction();
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/resolver/increment_decrement_validation_test.cc b/src/tint/resolver/increment_decrement_validation_test.cc
index 5061e49..a7e636f 100644
--- a/src/tint/resolver/increment_decrement_validation_test.cc
+++ b/src/tint/resolver/increment_decrement_validation_test.cc
@@ -161,7 +161,10 @@
     //   a++;
     // }
     auto* a = Param(Source{{12, 34}}, "a", ty.i32());
-    Func("func", {a}, ty.void_(), {Increment(Expr(Source{{56, 78}}, "a"))});
+    Func("func", utils::Vector{a}, ty.void_(),
+         utils::Vector{
+             Increment(Expr(Source{{56, 78}}, "a")),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify function parameter
@@ -175,7 +178,10 @@
     // {
     //   a++;
     // }
-    Func("func", {}, ty.i32(), {Return(0_i)});
+    Func("func", utils::Empty, ty.i32(),
+         utils::Vector{
+             Return(0_i),
+         });
     WrapInFunction(Increment(Call(Source{{56, 78}}, "func")));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/inferred_type_test.cc b/src/tint/resolver/inferred_type_test.cc
index a183b20..4051d82 100644
--- a/src/tint/resolver/inferred_type_test.cc
+++ b/src/tint/resolver/inferred_type_test.cc
@@ -83,7 +83,6 @@
     // const a = <type constructor>;
     auto* ctor_expr = params.create_value(*this, 0);
     auto* a = GlobalConst("a", nullptr, ctor_expr);
-    WrapInFunction();
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_EQ(TypeOf(a), expected_type);
@@ -97,7 +96,6 @@
     // var a = <type constructor>;
     auto* ctor_expr = params.create_value(*this, 0);
     auto* var = GlobalVar("a", nullptr, ast::StorageClass::kPrivate, ctor_expr);
-    WrapInFunction();
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
@@ -147,7 +145,7 @@
 
 TEST_F(ResolverInferredTypeTest, InferStruct_Pass) {
     auto* member = Member("x", ty.i32());
-    auto* str = Structure("S", {member});
+    auto* str = Structure("S", utils::Vector{member});
 
     auto* expected_type =
         create<sem::Struct>(str, str->name,
diff --git a/src/tint/resolver/is_storeable_test.cc b/src/tint/resolver/is_storeable_test.cc
index 80c3e20..0423f72 100644
--- a/src/tint/resolver/is_storeable_test.cc
+++ b/src/tint/resolver/is_storeable_test.cc
@@ -99,7 +99,7 @@
 }
 
 TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("a", ty.i32()),
                        Member("b", ty.f32()),
                    });
@@ -108,7 +108,7 @@
 }
 
 TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("a", ty.i32()),
                        Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
                    });
@@ -120,11 +120,11 @@
 }
 
 TEST_F(ResolverIsStorableTest, Struct_NestedStorable) {
-    auto* storable = Structure("Storable", {
+    auto* storable = Structure("Storable", utils::Vector{
                                                Member("a", ty.i32()),
                                                Member("b", ty.f32()),
                                            });
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("a", ty.i32()),
                        Member("b", ty.Of(storable)),
                    });
@@ -134,11 +134,11 @@
 
 TEST_F(ResolverIsStorableTest, Struct_NestedNonStorable) {
     auto* non_storable =
-        Structure("nonstorable", {
+        Structure("nonstorable", utils::Vector{
                                      Member("a", ty.i32()),
                                      Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
                                  });
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member("a", ty.i32()),
                        Member("b", ty.Of(non_storable)),
                    });
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index 70985dd..b68bad5 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -334,20 +334,20 @@
             WrapInFunction(Assign(Phony(), abstract_expr));
             break;
         case Method::kFnArg:
-            Func("F", {Param("P", target_ty())}, ty.void_(), {});
+            Func("F", utils::Vector{Param("P", target_ty())}, ty.void_(), utils::Empty);
             WrapInFunction(CallStmt(Call("F", abstract_expr)));
             break;
         case Method::kBuiltinArg:
             WrapInFunction(CallStmt(Call("min", target_expr(), abstract_expr)));
             break;
         case Method::kReturn:
-            Func("F", {}, target_ty(), {Return(abstract_expr)});
+            Func("F", utils::Empty, target_ty(), utils::Vector{Return(abstract_expr)});
             break;
         case Method::kArray:
             WrapInFunction(Construct(ty.array(target_ty(), 1_i), abstract_expr));
             break;
         case Method::kStruct:
-            Structure("S", {Member("v", target_ty())});
+            Structure("S", utils::Vector{Member("v", target_ty())});
             WrapInFunction(Construct(ty.type_name("S"), abstract_expr));
             break;
         case Method::kBinaryOp:
@@ -376,9 +376,9 @@
                                   DefaultCase()));
             break;
         case Method::kWorkgroupSize:
-            Func("f", {}, ty.void_(), {},
-                 {WorkgroupSize(target_expr(), abstract_expr, Expr(123_a)),
-                  Stage(ast::PipelineStage::kCompute)});
+            Func("f", utils::Empty, ty.void_(), utils::Empty,
+                 utils::Vector{WorkgroupSize(target_expr(), abstract_expr, Expr(123_a)),
+                               Stage(ast::PipelineStage::kCompute)});
             break;
         case Method::kRuntimeIndex:
             auto* runtime_index = Var("runtime_index", nullptr, Expr(1_i));
@@ -863,10 +863,10 @@
     const auto& method = std::get<1>(param);
     const auto& data = std::get<2>(param);
 
-    ast::ExpressionList abstract_exprs;
+    utils::Vector<const ast::Expression*, 4> abstract_exprs;
     auto abstract_expr = [&] {
         auto* expr = data.abstract_expr(*this, data.literal_value);
-        abstract_exprs.emplace_back(expr);
+        abstract_exprs.Push(expr);
         return expr;
     };
     switch (method) {
@@ -894,8 +894,9 @@
                                   DefaultCase()));
             break;
         case Method::kWorkgroupSize:
-            Func("f", {}, ty.void_(), {},
-                 {WorkgroupSize(abstract_expr()), Stage(ast::PipelineStage::kCompute)});
+            Func(
+                "f", utils::Empty, ty.void_(), utils::Empty,
+                utils::Vector{WorkgroupSize(abstract_expr()), Stage(ast::PipelineStage::kCompute)});
             break;
         case Method::kIndex:
             GlobalVar("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
@@ -1163,7 +1164,7 @@
 using MaterializeAbstractNumericToUnrelatedType = resolver::ResolverTest;
 
 TEST_F(MaterializeAbstractNumericToUnrelatedType, AIntToStructVarCtor) {
-    Structure("S", {Member("a", ty.i32())});
+    Structure("S", utils::Vector{Member("a", ty.i32())});
     WrapInFunction(Decl(Var("v", ty.type_name("S"), Expr(Source{{12, 34}}, 1_a))));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_THAT(
@@ -1172,7 +1173,7 @@
 }
 
 TEST_F(MaterializeAbstractNumericToUnrelatedType, AIntToStructLetCtor) {
-    Structure("S", {Member("a", ty.i32())});
+    Structure("S", utils::Vector{Member("a", ty.i32())});
     WrapInFunction(Decl(Let("v", ty.type_name("S"), Expr(Source{{12, 34}}, 1_a))));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_THAT(
diff --git a/src/tint/resolver/override_test.cc b/src/tint/resolver/override_test.cc
index f1e13e9..2615d73 100644
--- a/src/tint/resolver/override_test.cc
+++ b/src/tint/resolver/override_test.cc
@@ -50,7 +50,7 @@
 }
 
 TEST_F(ResolverOverrideTest, WithId) {
-    auto* a = Override("a", ty.f32(), Expr(1_f), {Id(7u)});
+    auto* a = Override("a", ty.f32(), Expr(1_f), utils::Vector{Id(7u)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -69,10 +69,10 @@
     std::vector<ast::Variable*> variables;
     auto* a = Override("a", ty.f32(), Expr(1_f));
     auto* b = Override("b", ty.f32(), Expr(1_f));
-    auto* c = Override("c", ty.f32(), Expr(1_f), {Id(2u)});
-    auto* d = Override("d", ty.f32(), Expr(1_f), {Id(4u)});
+    auto* c = Override("c", ty.f32(), Expr(1_f), utils::Vector{Id(2u)});
+    auto* d = Override("d", ty.f32(), Expr(1_f), utils::Vector{Id(4u)});
     auto* e = Override("e", ty.f32(), Expr(1_f));
-    auto* f = Override("f", ty.f32(), Expr(1_f), {Id(1u)});
+    auto* f = Override("f", ty.f32(), Expr(1_f), utils::Vector{Id(1u)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -86,8 +86,8 @@
 }
 
 TEST_F(ResolverOverrideTest, DuplicateIds) {
-    Override("a", ty.f32(), Expr(1_f), {Id(Source{{12, 34}}, 7u)});
-    Override("b", ty.f32(), Expr(1_f), {Id(Source{{56, 78}}, 7u)});
+    Override("a", ty.f32(), Expr(1_f), utils::Vector{Id(Source{{12, 34}}, 7u)});
+    Override("b", ty.f32(), Expr(1_f), utils::Vector{Id(Source{{56, 78}}, 7u)});
 
     EXPECT_FALSE(r()->Resolve());
 
@@ -96,7 +96,7 @@
 }
 
 TEST_F(ResolverOverrideTest, IdTooLarge) {
-    Override("a", ty.f32(), Expr(1_f), {Id(Source{{12, 34}}, 65536u)});
+    Override("a", ty.f32(), Expr(1_f), utils::Vector{Id(Source{{12, 34}}, 65536u)});
 
     EXPECT_FALSE(r()->Resolve());
 
@@ -106,7 +106,7 @@
 TEST_F(ResolverOverrideTest, F16_TemporallyBan) {
     Enable(ast::Extension::kF16);
 
-    Override(Source{{12, 34}}, "a", ty.f16(), Expr(1_h), {Id(1u)});
+    Override(Source{{12, 34}}, "a", ty.f16(), Expr(1_h), utils::Vector{Id(1u)});
 
     EXPECT_FALSE(r()->Resolve());
 
diff --git a/src/tint/resolver/ptr_ref_test.cc b/src/tint/resolver/ptr_ref_test.cc
index 037c6ff..0a757b4 100644
--- a/src/tint/resolver/ptr_ref_test.cc
+++ b/src/tint/resolver/ptr_ref_test.cc
@@ -57,17 +57,17 @@
 TEST_F(ResolverPtrRefTest, DefaultPtrStorageClass) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
-    auto* buf = Structure("S", {Member("m", ty.i32())});
+    auto* buf = Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* function = Var("f", ty.i32());
     auto* private_ = GlobalVar("p", ty.i32(), ast::StorageClass::kPrivate);
     auto* workgroup = GlobalVar("w", ty.i32(), ast::StorageClass::kWorkgroup);
     auto* uniform = GlobalVar("ub", ty.Of(buf), ast::StorageClass::kUniform,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(0u),
                                   create<ast::GroupAttribute>(0u),
                               });
     auto* storage = GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(1u),
                                   create<ast::GroupAttribute>(0u),
                               });
diff --git a/src/tint/resolver/ptr_ref_validation_test.cc b/src/tint/resolver/ptr_ref_validation_test.cc
index 768783b..5de5007 100644
--- a/src/tint/resolver/ptr_ref_validation_test.cc
+++ b/src/tint/resolver/ptr_ref_validation_test.cc
@@ -141,10 +141,10 @@
     // fn f() {
     //   let p : pointer<storage, i32> = &s.inner.arr[2i];
     // }
-    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+    auto* inner = Structure("Inner", utils::Vector{Member("arr", ty.array<i32, 4>())});
+    auto* buf = Structure("S", utils::Vector{Member("inner", ty.Of(inner))});
     auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(0u),
                                   create<ast::GroupAttribute>(0u),
                               });
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 8f85398..64c3e03 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -992,7 +992,7 @@
     return true;
 }
 
-bool Resolver::Statements(const ast::StatementList& stmts) {
+bool Resolver::Statements(utils::VectorRef<const ast::Statement*> stmts) {
     sem::Behaviors behaviors{sem::Behavior::kNext};
 
     bool reachable = true;
@@ -1058,7 +1058,7 @@
     auto* sem =
         builder_->create<sem::CaseStatement>(stmt, current_compound_statement_, current_function_);
     return StatementScope(stmt, sem, [&] {
-        sem->Selectors().reserve(stmt->selectors.size());
+        sem->Selectors().reserve(stmt->selectors.Length());
         for (auto* sel : stmt->selectors) {
             auto* expr = Expression(sel);
             if (!expr) {
@@ -1514,10 +1514,10 @@
 
     // Resolve all of the arguments, their types and the set of behaviors.
     utils::Vector<const sem::Expression*, 8> args;
-    args.Reserve(expr->args.size());
+    args.Reserve(expr->args.Length());
     auto args_stage = sem::EvaluationStage::kConstant;
     sem::Behaviors arg_behaviors;
-    for (size_t i = 0; i < expr->args.size(); i++) {
+    for (size_t i = 0; i < expr->args.Length(); i++) {
         auto* arg = sem_.Get(expr->args[i]);
         if (!arg) {
             return nullptr;
@@ -2422,7 +2422,7 @@
     return static_cast<uint32_t>(count);
 }
 
-bool Resolver::ArrayAttributes(const ast::AttributeList& attributes,
+bool Resolver::ArrayAttributes(utils::VectorRef<const ast::Attribute*> attributes,
                                const sem::Type* el_ty,
                                uint32_t& explicit_stride) {
     if (!validator_.NoDuplicateAttributes(attributes)) {
@@ -2493,7 +2493,7 @@
     }
 
     sem::StructMemberList sem_members;
-    sem_members.reserve(str->members.size());
+    sem_members.reserve(str->members.Length());
 
     // Calculate the effective size and alignment of each field, and the overall
     // size of the structure.
@@ -2692,8 +2692,8 @@
         utils::Vector<const sem::Type*, 8> types;
         types.Push(cond_ty);
 
-        std::vector<sem::CaseStatement*> cases;
-        cases.reserve(stmt->body.size());
+        utils::Vector<sem::CaseStatement*, 4> cases;
+        cases.Reserve(stmt->body.Length());
         for (auto* case_stmt : stmt->body) {
             Mark(case_stmt);
             auto* c = CaseStatement(case_stmt);
@@ -2703,7 +2703,7 @@
             for (auto* expr : c->Selectors()) {
                 types.Push(expr->Type()->UnwrapRef());
             }
-            cases.emplace_back(c);
+            cases.Push(c);
             behaviors.Add(c->Behaviors());
             sem->Cases().emplace_back(c);
         }
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index ecbda30..10b5806 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -160,19 +160,6 @@
     /// ProgramBuilder.
     void CreateSemanticNodes() const;
 
-    /// Retrieves information for the requested import.
-    /// @param src the source of the import
-    /// @param path the import path
-    /// @param name the method name to get information on
-    /// @param params the parameters to the method call
-    /// @param id out parameter for the external call ID. Must not be a nullptr.
-    /// @returns the return type of `name` in `path` or nullptr on error.
-    sem::Type* GetImportData(const Source& src,
-                             const std::string& path,
-                             const std::string& name,
-                             const ast::ExpressionList& params,
-                             uint32_t* id);
-
     /// Expression traverses the graph of expressions starting at `expr`, building a postordered
     /// list (leaf-first) of all the expression nodes. Each of the expressions are then resolved by
     /// dispatching to the appropriate expression handlers below.
@@ -265,7 +252,7 @@
     sem::Statement* Statement(const ast::Statement*);
     sem::SwitchStatement* SwitchStatement(const ast::SwitchStatement* s);
     sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
-    bool Statements(const ast::StatementList&);
+    bool Statements(utils::VectorRef<const ast::Statement*>);
 
     // CollectTextureSamplerPairs() collects all the texture/sampler pairs from the target function
     // / builtin, and records these on the current function by calling AddTextureSamplerPair().
@@ -309,7 +296,7 @@
     /// @param el_ty the element type of the array.
     /// @param explicit_stride assigned the specified stride of the array in bytes.
     /// @returns true on success, false on failure
-    bool ArrayAttributes(const ast::AttributeList& attributes,
+    bool ArrayAttributes(utils::VectorRef<const ast::Attribute*> attributes,
                          const sem::Type* el_ty,
                          uint32_t& explicit_stride);
 
diff --git a/src/tint/resolver/resolver_behavior_test.cc b/src/tint/resolver/resolver_behavior_test.cc
index b125e39..43f0bf1 100644
--- a/src/tint/resolver/resolver_behavior_test.cc
+++ b/src/tint/resolver/resolver_behavior_test.cc
@@ -33,8 +33,8 @@
         // Create a function called 'DiscardOrNext' which returns an i32, and has
         // the behavior of {Discard, Return}, which when called, will have the
         // behavior {Discard, Next}.
-        Func("DiscardOrNext", {}, ty.i32(),
-             {
+        Func("DiscardOrNext", utils::Empty, ty.i32(),
+             utils::Vector{
                  If(true, Block(Discard())),
                  Return(1_i),
              });
@@ -72,8 +72,8 @@
 }
 
 TEST_F(ResolverBehaviorTest, ExprIndex_Arr) {
-    Func("ArrayDiscardOrNext", {}, ty.array<i32, 4>(),
-         {
+    Func("ArrayDiscardOrNext", utils::Empty, ty.array<i32, 4>(),
+         utils::Vector{
              If(true, Block(Discard())),
              Return(Construct(ty.array<i32, 4>())),
          });
@@ -165,7 +165,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtCallReturn) {
-    Func("f", {}, ty.void_(), {Return()});
+    Func("f", utils::Empty, ty.void_(), utils::Vector{Return()});
     auto* stmt = CallStmt(Call("f"));
     WrapInFunction(stmt);
 
@@ -176,7 +176,7 @@
 }
 
 TEST_F(ResolverBehaviorTest, StmtCallFuncDiscard) {
-    Func("f", {}, ty.void_(), {Discard()});
+    Func("f", utils::Empty, ty.void_(), utils::Vector{Discard()});
     auto* stmt = CallStmt(Call("f"));
     WrapInFunction(stmt);
 
@@ -522,7 +522,7 @@
 
 TEST_F(ResolverBehaviorTest, StmtReturn_DiscardOrNext) {
     auto* stmt = Return(Call("DiscardOrNext"));
-    Func("F", {}, ty.i32(), {stmt});
+    Func("F", utils::Empty, ty.i32(), utils::Vector{stmt});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index bacbcd6..ecc251a 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -233,7 +233,7 @@
     auto* cond = Expr(2_i);
 
     auto* ret = Return(cond);
-    Func("test", {}, ty.i32(), {ret}, {});
+    Func("test", utils::Empty, ty.i32(), utils::Vector{ret}, utils::Empty);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -270,8 +270,8 @@
 }
 
 TEST_F(ResolverTest, Stmt_Call) {
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Return(),
          });
 
@@ -357,7 +357,7 @@
     auto* bar_f32_init = bar_f32->constructor;
     auto* bar_f32_decl = Decl(bar_f32);
 
-    Func("func", {}, ty.void_(), {inner, foo_f32_decl, bar_f32_decl});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{inner, foo_f32_decl, bar_f32_decl});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_NE(TypeOf(foo_i32_init), nullptr);
@@ -372,8 +372,8 @@
     EXPECT_EQ(StmtOf(bar_i32_init), bar_i32_decl);
     EXPECT_EQ(StmtOf(foo_f32_init), foo_f32_decl);
     EXPECT_EQ(StmtOf(bar_f32_init), bar_f32_decl);
-    EXPECT_TRUE(CheckVarUsers(foo_i32, {bar_i32->constructor}));
-    EXPECT_TRUE(CheckVarUsers(foo_f32, {bar_f32->constructor}));
+    EXPECT_TRUE(CheckVarUsers(foo_i32, utils::Vector{bar_i32->constructor}));
+    EXPECT_TRUE(CheckVarUsers(foo_f32, utils::Vector{bar_f32->constructor}));
     ASSERT_NE(VarOf(bar_i32->constructor), nullptr);
     EXPECT_EQ(VarOf(bar_i32->constructor)->Declaration(), foo_i32);
     ASSERT_NE(VarOf(bar_f32->constructor), nullptr);
@@ -393,7 +393,7 @@
     auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
     auto* fn_i32_init = fn_i32->constructor;
     auto* fn_i32_decl = Decl(fn_i32);
-    Func("func_i32", {}, ty.void_(), {fn_i32_decl});
+    Func("func_i32", utils::Empty, ty.void_(), utils::Vector{fn_i32_decl});
 
     // Declare f32 "foo" at module scope
     auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kPrivate, Expr(2_f));
@@ -404,7 +404,7 @@
     auto* fn_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
     auto* fn_f32_init = fn_f32->constructor;
     auto* fn_f32_decl = Decl(fn_f32);
-    Func("func_f32", {}, ty.void_(), {fn_f32_decl});
+    Func("func_f32", utils::Empty, ty.void_(), utils::Vector{fn_f32_decl});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_NE(TypeOf(mod_init), nullptr);
@@ -416,8 +416,8 @@
     EXPECT_EQ(StmtOf(fn_i32_init), fn_i32_decl);
     EXPECT_EQ(StmtOf(mod_init), nullptr);
     EXPECT_EQ(StmtOf(fn_f32_init), fn_f32_decl);
-    EXPECT_TRUE(CheckVarUsers(fn_i32, {}));
-    EXPECT_TRUE(CheckVarUsers(mod_f32, {fn_f32->constructor}));
+    EXPECT_TRUE(CheckVarUsers(fn_i32, utils::Empty));
+    EXPECT_TRUE(CheckVarUsers(mod_f32, utils::Vector{fn_f32->constructor}));
     ASSERT_NE(VarOf(fn_f32->constructor), nullptr);
     EXPECT_EQ(VarOf(fn_f32->constructor)->Declaration(), mod_f32);
 }
@@ -491,7 +491,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Call) {
-    Func("my_func", {}, ty.f32(), {Return(0_f)});
+    Func("my_func", utils::Empty, ty.f32(), utils::Vector{Return(0_f)});
 
     auto* call = Call("my_func");
     WrapInFunction(call);
@@ -503,7 +503,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Call_InBinaryOp) {
-    Func("func", {}, ty.f32(), {Return(0_f)});
+    Func("func", utils::Empty, ty.f32(), utils::Vector{Return(0_f)});
 
     auto* expr = Add(Call("func"), Call("func"));
     WrapInFunction(expr);
@@ -515,8 +515,8 @@
 }
 
 TEST_F(ResolverTest, Expr_Call_WithParams) {
-    Func("my_func", {Param(Sym(), ty.f32())}, ty.f32(),
-         {
+    Func("my_func", utils::Vector{Param(Sym(), ty.f32())}, ty.f32(),
+         utils::Vector{
              Return(1.2_f),
          });
 
@@ -610,7 +610,7 @@
     ASSERT_NE(TypeOf(ident), nullptr);
     ASSERT_TRUE(TypeOf(ident)->Is<sem::Reference>());
     EXPECT_TRUE(TypeOf(ident)->UnwrapRef()->Is<sem::F32>());
-    EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
+    EXPECT_TRUE(CheckVarUsers(my_var, utils::Vector{ident}));
     ASSERT_NE(VarOf(ident), nullptr);
     EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
 }
@@ -625,7 +625,7 @@
 
     ASSERT_NE(TypeOf(ident), nullptr);
     EXPECT_TRUE(TypeOf(ident)->Is<sem::F32>());
-    EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
+    EXPECT_TRUE(CheckVarUsers(my_var, utils::Vector{ident}));
     ASSERT_NE(VarOf(ident), nullptr);
     EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
 }
@@ -635,8 +635,8 @@
     auto* var = Let("my_var", ty.f32(), Construct(ty.f32()));
     auto* decl = Decl(Var("b", ty.f32(), ast::StorageClass::kNone, my_var_a));
 
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
              decl,
          });
@@ -646,7 +646,7 @@
     ASSERT_NE(TypeOf(my_var_a), nullptr);
     EXPECT_TRUE(TypeOf(my_var_a)->Is<sem::F32>());
     EXPECT_EQ(StmtOf(my_var_a), decl);
-    EXPECT_TRUE(CheckVarUsers(var, {my_var_a}));
+    EXPECT_TRUE(CheckVarUsers(var, utils::Vector{my_var_a}));
     ASSERT_NE(VarOf(my_var_a), nullptr);
     EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
 }
@@ -658,8 +658,8 @@
     auto* a = Var("a", ty.array<bool, 10>(), array<bool, 10>());
     auto* idx = Var("idx", ty.f32(), Construct(ty.f32()));
     auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(a),
              Decl(idx),
              Decl(f),
@@ -676,8 +676,8 @@
 
     auto* var = Var("my_var", ty.f32());
 
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
              assign,
          });
@@ -692,7 +692,7 @@
     ASSERT_TRUE(TypeOf(my_var_b)->Is<sem::Reference>());
     EXPECT_TRUE(TypeOf(my_var_b)->UnwrapRef()->Is<sem::F32>());
     EXPECT_EQ(StmtOf(my_var_b), assign);
-    EXPECT_TRUE(CheckVarUsers(var, {my_var_a, my_var_b}));
+    EXPECT_TRUE(CheckVarUsers(var, utils::Vector{my_var_a, my_var_b}));
     ASSERT_NE(VarOf(my_var_a), nullptr);
     EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
     ASSERT_NE(VarOf(my_var_b), nullptr);
@@ -705,8 +705,8 @@
     auto* v_decl = Decl(Var("v", ty.f32()));
     auto* p_decl = Decl(Let("p", ty.pointer<f32>(ast::StorageClass::kFunction), AddressOf(v)));
     auto* assign = Assign(Deref(p), 1.23_f);
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              v_decl,
              p_decl,
              assign,
@@ -725,8 +725,8 @@
 }
 
 TEST_F(ResolverTest, Expr_Call_Function) {
-    Func("my_func", {}, ty.f32(),
-         {
+    Func("my_func", utils::Empty, ty.f32(),
+         utils::Vector{
              Return(0_f),
          });
 
@@ -752,12 +752,12 @@
     auto* param_c = Param("c", ty.u32());
 
     auto* func = Func("my_func",
-                      {
+                      utils::Vector{
                           param_a,
                           param_b,
                           param_c,
                       },
-                      ty.void_(), {});
+                      ty.void_(), utils::Empty);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -774,19 +774,19 @@
 }
 
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
-    auto* s = Structure("S", {Member("m", ty.u32())});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.u32())});
 
     auto* sb_var =
         GlobalVar("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                  ast::AttributeList{
+                  utils::Vector{
                       create<ast::BindingAttribute>(0u),
                       create<ast::GroupAttribute>(0u),
                   });
     auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
-    auto* func = Func("my_func", {}, ty.void_(),
-                      {
+    auto* func = Func("my_func", utils::Empty, ty.void_(),
+                      utils::Vector{
                           Assign("wg_var", "wg_var"),
                           Assign("sb_var", "sb_var"),
                           Assign("priv_var", "priv_var"),
@@ -807,26 +807,26 @@
 }
 
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
-    auto* s = Structure("S", {Member("m", ty.u32())});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.u32())});
 
     auto* sb_var =
         GlobalVar("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                  ast::AttributeList{
+                  utils::Vector{
                       create<ast::BindingAttribute>(0u),
                       create<ast::GroupAttribute>(0u),
                   });
     auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
-    Func("my_func", {}, ty.f32(),
-         {Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"), Assign("priv_var", "priv_var"),
-          Return(0_f)});
+    Func("my_func", utils::Empty, ty.f32(),
+         utils::Vector{Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"),
+                       Assign("priv_var", "priv_var"), Return(0_f)});
 
-    auto* func2 = Func("func", {}, ty.void_(),
-                       {
+    auto* func2 = Func("func", utils::Empty, ty.void_(),
+                       utils::Vector{
                            WrapInStatement(Call("my_func")),
                        },
-                       ast::AttributeList{});
+                       utils::Empty);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -842,8 +842,8 @@
 }
 
 TEST_F(ResolverTest, Function_NotRegisterFunctionVariable) {
-    auto* func = Func("my_func", {}, ty.void_(),
-                      {
+    auto* func = Func("my_func", utils::Empty, ty.void_(),
+                      utils::Vector{
                           Decl(Var("var", ty.f32())),
                           Assign("var", 1_f),
                       });
@@ -858,8 +858,8 @@
 }
 
 TEST_F(ResolverTest, Function_NotRegisterFunctionConstant) {
-    auto* func = Func("my_func", {}, ty.void_(),
-                      {
+    auto* func = Func("my_func", utils::Empty, ty.void_(),
+                      utils::Vector{
                           Decl(Let("var", ty.f32(), Construct(ty.f32()))),
                       });
 
@@ -873,7 +873,7 @@
 }
 
 TEST_F(ResolverTest, Function_NotRegisterFunctionParams) {
-    auto* func = Func("my_func", {Param("var", ty.f32())}, ty.void_(), {});
+    auto* func = Func("my_func", utils::Vector{Param("var", ty.f32())}, ty.void_(), utils::Empty);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* func_sem = Sem().Get(func);
@@ -884,12 +884,12 @@
 }
 
 TEST_F(ResolverTest, Function_CallSites) {
-    auto* foo = Func("foo", {}, ty.void_(), {});
+    auto* foo = Func("foo", utils::Empty, ty.void_(), utils::Empty);
 
     auto* call_1 = Call("foo");
     auto* call_2 = Call("foo");
-    auto* bar = Func("bar", {}, ty.void_(),
-                     {
+    auto* bar = Func("bar", utils::Empty, ty.void_(),
+                     utils::Vector{
                          CallStmt(call_1),
                          CallStmt(call_2),
                      });
@@ -910,7 +910,7 @@
 TEST_F(ResolverTest, Function_WorkgroupSize_NotSet) {
     // @compute @workgroup_size(1)
     // fn main() {}
-    auto* func = Func("main", {}, ty.void_(), {});
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -928,8 +928,8 @@
 TEST_F(ResolverTest, Function_WorkgroupSize_Literals) {
     // @compute @workgroup_size(8, 2, 3)
     // fn main() {}
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize(8_i, 2_i, 3_i),
                       });
@@ -956,8 +956,8 @@
     GlobalConst("width", ty.i32(), Expr(16_i));
     GlobalConst("height", ty.i32(), Expr(8_i));
     GlobalConst("depth", ty.i32(), Expr(2_i));
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize("width", "height", "depth"),
                       });
@@ -984,8 +984,8 @@
                 Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 8_i))));
     GlobalConst("height", ty.i32(),
                 Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 4_i))));
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize("width", "height"),
                       });
@@ -1009,11 +1009,11 @@
     // @id(2) override depth = 2i;
     // @compute @workgroup_size(width, height, depth)
     // fn main() {}
-    auto* width = Override("width", ty.i32(), Expr(16_i), {Id(0)});
-    auto* height = Override("height", ty.i32(), Expr(8_i), {Id(1)});
-    auto* depth = Override("depth", ty.i32(), Expr(2_i), {Id(2)});
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {
+    auto* width = Override("width", ty.i32(), Expr(16_i), utils::Vector{Id(0)});
+    auto* height = Override("height", ty.i32(), Expr(8_i), utils::Vector{Id(1)});
+    auto* depth = Override("depth", ty.i32(), Expr(2_i), utils::Vector{Id(2)});
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize("width", "height", "depth"),
                       });
@@ -1037,11 +1037,11 @@
     // @id(2) override depth : i32;
     // @compute @workgroup_size(width, height, depth)
     // fn main() {}
-    auto* width = Override("width", ty.i32(), nullptr, {Id(0)});
-    auto* height = Override("height", ty.i32(), nullptr, {Id(1)});
-    auto* depth = Override("depth", ty.i32(), nullptr, {Id(2)});
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {
+    auto* width = Override("width", ty.i32(), nullptr, utils::Vector{Id(0)});
+    auto* height = Override("height", ty.i32(), nullptr, utils::Vector{Id(1)});
+    auto* depth = Override("depth", ty.i32(), nullptr, utils::Vector{Id(2)});
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize("width", "height", "depth"),
                       });
@@ -1064,10 +1064,10 @@
     // const depth = 3i;
     // @compute @workgroup_size(8, height, depth)
     // fn main() {}
-    auto* height = Override("height", ty.i32(), Expr(2_i), {Id(0)});
+    auto* height = Override("height", ty.i32(), Expr(2_i), utils::Vector{Id(0)});
     GlobalConst("depth", ty.i32(), Expr(3_i));
-    auto* func = Func("main", {}, ty.void_(), {},
-                      {
+    auto* func = Func("main", utils::Empty, ty.void_(), utils::Empty,
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize(8_i, "height", "depth"),
                       });
@@ -1086,8 +1086,8 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
-    auto* st =
-        Structure("S", {Member("first_member", ty.i32()), Member("second_member", ty.f32())});
+    auto* st = Structure(
+        "S", utils::Vector{Member("first_member", ty.i32()), Member("second_member", ty.f32())});
     GlobalVar("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
 
     auto* mem = MemberAccessor("my_struct", "second_member");
@@ -1109,8 +1109,8 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_Struct_Alias) {
-    auto* st =
-        Structure("S", {Member("first_member", ty.i32()), Member("second_member", ty.f32())});
+    auto* st = Structure(
+        "S", utils::Vector{Member("first_member", ty.i32()), Member("second_member", ty.f32())});
     auto* alias = Alias("alias", ty.Of(st));
     GlobalVar("my_struct", ty.Of(alias), ast::StorageClass::kPrivate);
 
@@ -1184,8 +1184,8 @@
     // }
     //
 
-    auto* stB = Structure("B", {Member("foo", ty.vec4<f32>())});
-    auto* stA = Structure("A", {Member("mem", ty.array(ty.Of(stB), 3_i))});
+    auto* stB = Structure("B", utils::Vector{Member("foo", ty.vec4<f32>())});
+    auto* stA = Structure("A", utils::Vector{Member("mem", ty.array(ty.Of(stB), 3_i))});
     GlobalVar("c", ty.Of(stA), ast::StorageClass::kPrivate);
 
     auto* mem =
@@ -1202,8 +1202,8 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_InBinaryOp) {
-    auto* st =
-        Structure("S", {Member("first_member", ty.f32()), Member("second_member", ty.f32())});
+    auto* st = Structure(
+        "S", utils::Vector{Member("first_member", ty.f32()), Member("second_member", ty.f32())});
     GlobalVar("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
 
     auto* expr = Add(MemberAccessor("my_struct", "first_member"),
@@ -1726,7 +1726,7 @@
     auto* var = Var("var", ty.i32());
 
     auto* stmt = Decl(var);
-    Func("func", {}, ty.void_(), {stmt});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{stmt});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1736,7 +1736,7 @@
 TEST_F(ResolverTest, StorageClass_SetForSampler) {
     auto* t = ty.sampler(ast::SamplerKind::kSampler);
     auto* var = GlobalVar("var", t,
-                          ast::AttributeList{
+                          utils::Vector{
                               create<ast::BindingAttribute>(0u),
                               create<ast::GroupAttribute>(0u),
                           });
@@ -1749,7 +1749,7 @@
 TEST_F(ResolverTest, StorageClass_SetForTexture) {
     auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
     auto* var = GlobalVar("var", t,
-                          ast::AttributeList{
+                          utils::Vector{
                               create<ast::BindingAttribute>(0u),
                               create<ast::GroupAttribute>(0u),
                           });
@@ -1762,7 +1762,7 @@
 TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
     auto* var = Let("var", ty.i32(), Construct(ty.i32()));
     auto* stmt = Decl(var);
-    Func("func", {}, ty.void_(), {stmt});
+    Func("func", utils::Empty, ty.void_(), utils::Vector{stmt});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1772,9 +1772,9 @@
 TEST_F(ResolverTest, Access_SetForStorageBuffer) {
     // struct S { x : i32 };
     // var<storage> g : S;
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     auto* var = GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
-                          ast::AttributeList{
+                          utils::Vector{
                               create<ast::BindingAttribute>(0u),
                               create<ast::GroupAttribute>(0u),
                           });
@@ -1789,10 +1789,10 @@
     // @group(3) @binding(4) var s2 : sampler;
     auto* s1 = GlobalVar(
         Sym(), ty.sampler(ast::SamplerKind::kSampler),
-        ast::AttributeList{create<ast::GroupAttribute>(1u), create<ast::BindingAttribute>(2u)});
+        utils::Vector{create<ast::GroupAttribute>(1u), create<ast::BindingAttribute>(2u)});
     auto* s2 = GlobalVar(
         Sym(), ty.sampler(ast::SamplerKind::kSampler),
-        ast::AttributeList{create<ast::GroupAttribute>(3u), create<ast::BindingAttribute>(4u)});
+        utils::Vector{create<ast::GroupAttribute>(3u), create<ast::BindingAttribute>(4u)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1819,37 +1819,37 @@
     GlobalVar("call_b", ty.f32(), ast::StorageClass::kPrivate);
     GlobalVar("call_c", ty.f32(), ast::StorageClass::kPrivate);
 
-    auto* func_b = Func("b", {}, ty.f32(),
-                        {
+    auto* func_b = Func("b", utils::Empty, ty.f32(),
+                        utils::Vector{
                             Return(0_f),
                         });
-    auto* func_c = Func("c", {}, ty.f32(),
-                        {
+    auto* func_c = Func("c", utils::Empty, ty.f32(),
+                        utils::Vector{
                             Assign("second", Call("b")),
                             Return(0_f),
                         });
 
-    auto* func_a = Func("a", {}, ty.f32(),
-                        {
+    auto* func_a = Func("a", utils::Empty, ty.f32(),
+                        utils::Vector{
                             Assign("first", Call("c")),
                             Return(0_f),
                         });
 
-    auto* ep_1 = Func("ep_1", {}, ty.void_(),
-                      {
+    auto* ep_1 = Func("ep_1", utils::Empty, ty.void_(),
+                      utils::Vector{
                           Assign("call_a", Call("a")),
                           Assign("call_b", Call("b")),
                       },
-                      {
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize(1_i),
                       });
 
-    auto* ep_2 = Func("ep_2", {}, ty.void_(),
-                      {
+    auto* ep_2 = Func("ep_2", utils::Empty, ty.void_(),
+                      utils::Vector{
                           Assign("call_c", Call("c")),
                       },
-                      {
+                      utils::Vector{
                           Stage(ast::PipelineStage::kCompute),
                           WorkgroupSize(1_i),
                       });
@@ -1906,38 +1906,38 @@
     auto fn_a = [](int level) { return "l" + std::to_string(level + 1) + "a"; };
     auto fn_b = [](int level) { return "l" + std::to_string(level + 1) + "b"; };
 
-    Func(fn_a(levels), {}, ty.void_(), {});
-    Func(fn_b(levels), {}, ty.void_(), {});
+    Func(fn_a(levels), utils::Empty, ty.void_(), utils::Empty);
+    Func(fn_b(levels), utils::Empty, ty.void_(), utils::Empty);
 
     for (int i = levels - 1; i >= 0; i--) {
-        Func(fn_a(i), {}, ty.void_(),
-             {
+        Func(fn_a(i), utils::Empty, ty.void_(),
+             utils::Vector{
                  CallStmt(Call(fn_a(i + 1))),
                  CallStmt(Call(fn_b(i + 1))),
              },
-             {});
-        Func(fn_b(i), {}, ty.void_(),
-             {
+             utils::Empty);
+        Func(fn_b(i), utils::Empty, ty.void_(),
+             utils::Vector{
                  CallStmt(Call(fn_a(i + 1))),
                  CallStmt(Call(fn_b(i + 1))),
              },
-             {});
+             utils::Empty);
     }
 
-    Func("main", {}, ty.void_(),
-         {
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              CallStmt(Call(fn_a(0))),
              CallStmt(Call(fn_b(0))),
          },
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+         utils::Vector{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 // Test for crbug.com/tint/728
 TEST_F(ResolverTest, ASTNodesAreReached) {
-    Structure("A", {Member("x", ty.array<f32, 4>(4))});
-    Structure("B", {Member("x", ty.array<f32, 4>(4))});
+    Structure("A", utils::Vector{Member("x", ty.array<f32, 4>(4))});
+    Structure("B", utils::Vector{Member("x", ty.array<f32, 4>(4))});
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -1999,8 +1999,8 @@
     GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
 
     auto* call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
-    const ast::Function* f =
-        Func("test_function", {}, ty.void_(), {call}, {Stage(ast::PipelineStage::kFragment)});
+    const ast::Function* f = Func("test_function", utils::Empty, ty.void_(), utils::Vector{call},
+                                  utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -2016,10 +2016,12 @@
     GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
 
     auto* inner_call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
-    const ast::Function* inner_func = Func("inner_func", {}, ty.void_(), {inner_call});
+    const ast::Function* inner_func =
+        Func("inner_func", utils::Empty, ty.void_(), utils::Vector{inner_call});
     auto* outer_call = CallStmt(Call("inner_func"));
     const ast::Function* outer_func =
-        Func("outer_func", {}, ty.void_(), {outer_call}, {Stage(ast::PipelineStage::kFragment)});
+        Func("outer_func", utils::Empty, ty.void_(), utils::Vector{outer_call},
+             utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -2039,14 +2041,16 @@
     GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
 
     auto* inner_call_1 = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
-    const ast::Function* inner_func_1 = Func("inner_func_1", {}, ty.void_(), {inner_call_1});
+    const ast::Function* inner_func_1 =
+        Func("inner_func_1", utils::Empty, ty.void_(), utils::Vector{inner_call_1});
     auto* inner_call_2 = CallStmt(Call("textureSample", "t", "s", vec2<f32>(3_f, 4_f)));
-    const ast::Function* inner_func_2 = Func("inner_func_2", {}, ty.void_(), {inner_call_2});
+    const ast::Function* inner_func_2 =
+        Func("inner_func_2", utils::Empty, ty.void_(), utils::Vector{inner_call_2});
     auto* outer_call_1 = CallStmt(Call("inner_func_1"));
     auto* outer_call_2 = CallStmt(Call("inner_func_2"));
     const ast::Function* outer_func =
-        Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
-             {Stage(ast::PipelineStage::kFragment)});
+        Func("outer_func", utils::Empty, ty.void_(), utils::Vector{outer_call_1, outer_call_2},
+             utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -2074,14 +2078,16 @@
     GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 3));
 
     auto* inner_call_1 = CallStmt(Call("textureSample", "t1", "s", vec2<f32>(1_f, 2_f)));
-    const ast::Function* inner_func_1 = Func("inner_func_1", {}, ty.void_(), {inner_call_1});
+    const ast::Function* inner_func_1 =
+        Func("inner_func_1", utils::Empty, ty.void_(), utils::Vector{inner_call_1});
     auto* inner_call_2 = CallStmt(Call("textureSample", "t2", "s", vec2<f32>(3_f, 4_f)));
-    const ast::Function* inner_func_2 = Func("inner_func_2", {}, ty.void_(), {inner_call_2});
+    const ast::Function* inner_func_2 =
+        Func("inner_func_2", utils::Empty, ty.void_(), utils::Vector{inner_call_2});
     auto* outer_call_1 = CallStmt(Call("inner_func_1"));
     auto* outer_call_2 = CallStmt(Call("inner_func_2"));
     const ast::Function* outer_func =
-        Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
-             {Stage(ast::PipelineStage::kFragment)});
+        Func("outer_func", utils::Empty, ty.void_(), utils::Vector{outer_call_1, outer_call_2},
+             utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -2119,18 +2125,18 @@
 }
 
 TEST_F(ResolverTest, ModuleDependencyOrderedDeclarations) {
-    auto* f0 = Func("f0", {}, ty.void_(), {});
+    auto* f0 = Func("f0", utils::Empty, ty.void_(), utils::Empty);
     auto* v0 = GlobalVar("v0", ty.i32(), ast::StorageClass::kPrivate);
     auto* a0 = Alias("a0", ty.i32());
-    auto* s0 = Structure("s0", {Member("m", ty.i32())});
-    auto* f1 = Func("f1", {}, ty.void_(), {});
+    auto* s0 = Structure("s0", utils::Vector{Member("m", ty.i32())});
+    auto* f1 = Func("f1", utils::Empty, ty.void_(), utils::Empty);
     auto* v1 = GlobalVar("v1", ty.i32(), ast::StorageClass::kPrivate);
     auto* a1 = Alias("a1", ty.i32());
-    auto* s1 = Structure("s1", {Member("m", ty.i32())});
-    auto* f2 = Func("f2", {}, ty.void_(), {});
+    auto* s1 = Structure("s1", utils::Vector{Member("m", ty.i32())});
+    auto* f2 = Func("f2", utils::Empty, ty.void_(), utils::Empty);
     auto* v2 = GlobalVar("v2", ty.i32(), ast::StorageClass::kPrivate);
     auto* a2 = Alias("a2", ty.i32());
-    auto* s2 = Structure("s2", {Member("m", ty.i32())});
+    auto* s2 = Structure("s2", utils::Vector{Member("m", ty.i32())});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index efb215b..bab1d84 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -18,7 +18,6 @@
 #include <functional>
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "gtest/gtest.h"
 #include "src/tint/program_builder.h"
@@ -28,6 +27,7 @@
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/utils/vector.h"
 
 namespace tint::resolver {
 
@@ -88,9 +88,9 @@
     /// @param expected_users the expected users of the variable
     /// @return true if all users are as expected
     bool CheckVarUsers(const ast::Variable* var,
-                       std::vector<const ast::Expression*>&& expected_users) {
+                       utils::VectorRef<const ast::Expression*> expected_users) {
         auto& var_users = Sem().Get(var)->Users();
-        if (var_users.size() != expected_users.size()) {
+        if (var_users.size() != expected_users.Length()) {
             return false;
         }
         for (size_t i = 0; i < var_users.size(); i++) {
@@ -389,10 +389,10 @@
     /// @param b the ProgramBuilder
     /// @param elem_value the value each element will be initialized with
     /// @return the list of expressions that are used to construct the vector
-    static inline ast::ExpressionList ExprArgs(ProgramBuilder& b, double elem_value) {
-        ast::ExpressionList args;
+    static inline auto ExprArgs(ProgramBuilder& b, double elem_value) {
+        utils::Vector<const ast::Expression*, N> args;
         for (uint32_t i = 0; i < N; i++) {
-            args.emplace_back(DataType<T>::Expr(b, elem_value));
+            args.Push(DataType<T>::Expr(b, elem_value));
         }
         return args;
     }
@@ -433,10 +433,10 @@
     /// @param b the ProgramBuilder
     /// @param elem_value the value each element will be initialized with
     /// @return the list of expressions that are used to construct the matrix
-    static inline ast::ExpressionList ExprArgs(ProgramBuilder& b, double elem_value) {
-        ast::ExpressionList args;
+    static inline auto ExprArgs(ProgramBuilder& b, double elem_value) {
+        utils::Vector<const ast::Expression*, N> args;
         for (uint32_t i = 0; i < N; i++) {
-            args.emplace_back(DataType<vec<M, T>>::Expr(b, elem_value));
+            args.Push(DataType<vec<M, T>>::Expr(b, elem_value));
         }
         return args;
     }
@@ -566,10 +566,10 @@
     /// @param b the ProgramBuilder
     /// @param elem_value the value each element will be initialized with
     /// @return the list of expressions that are used to construct the array
-    static inline ast::ExpressionList ExprArgs(ProgramBuilder& b, double elem_value) {
-        ast::ExpressionList args;
+    static inline auto ExprArgs(ProgramBuilder& b, double elem_value) {
+        utils::Vector<const ast::Expression*, N> args;
         for (uint32_t i = 0; i < N; i++) {
-            args.emplace_back(DataType<T>::Expr(b, elem_value));
+            args.Push(DataType<T>::Expr(b, elem_value));
         }
         return args;
     }
diff --git a/src/tint/resolver/side_effects_test.cc b/src/tint/resolver/side_effects_test.cc
index 689c7b8..445fedd 100644
--- a/src/tint/resolver/side_effects_test.cc
+++ b/src/tint/resolver/side_effects_test.cc
@@ -31,8 +31,8 @@
         auto global = Sym();
         GlobalVar(global, ty.Of<T>(), ast::StorageClass::kPrivate);
         auto local = Sym();
-        Func(name, {}, ty.Of<T>(),
-             {
+        Func(name, utils::Empty, ty.Of<T>(),
+             utils::Vector{
                  Decl(Var(local, ty.Of<T>())),
                  Assign(global, local),
                  Return(global),
@@ -44,8 +44,8 @@
         auto global = Sym();
         GlobalVar(global, make_type(), ast::StorageClass::kPrivate);
         auto local = Sym();
-        Func(name, {}, make_type(),
-             {
+        Func(name, utils::Empty, make_type(),
+             utils::Vector{
                  Decl(Var(local, make_type())),
                  Assign(global, local),
                  Return(global),
@@ -89,8 +89,8 @@
 TEST_F(SideEffectsTest, Call_Builtin_NoSE) {
     GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
     auto* expr = Call("dpdx", "a");
-    Func("f", {}, ty.void_(), {Ignore(expr)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("f", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
+         utils::Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     auto* sem = Sem().Get(expr);
@@ -102,8 +102,8 @@
 TEST_F(SideEffectsTest, Call_Builtin_NoSE_WithSEArg) {
     MakeSideEffectFunc<f32>("se");
     auto* expr = Call("dpdx", Call("se"));
-    Func("f", {}, ty.void_(), {Ignore(expr)},
-         {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+    Func("f", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
+         utils::Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     auto* sem = Sem().Get(expr);
@@ -125,7 +125,7 @@
 }
 
 TEST_F(SideEffectsTest, Call_Function) {
-    Func("f", {}, ty.i32(), {Return(1_i)});
+    Func("f", utils::Empty, ty.i32(), utils::Vector{Return(1_i)});
     auto* expr = Call("f");
     WrapInFunction(expr);
 
@@ -185,7 +185,7 @@
 }
 
 TEST_F(SideEffectsTest, MemberAccessor_Struct_NoSE) {
-    auto* s = Structure("S", {Member("m", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* var = Decl(Var("a", ty.Of(s)));
     auto* expr = MemberAccessor("a", "m");
     WrapInFunction(var, expr);
@@ -197,7 +197,7 @@
 }
 
 TEST_F(SideEffectsTest, MemberAccessor_Struct_SE) {
-    auto* s = Structure("S", {Member("m", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.i32())});
     MakeSideEffectFunc("se", [&] { return ty.Of(s); });
     auto* expr = MemberAccessor(Call("se"), "m");
     WrapInFunction(expr);
diff --git a/src/tint/resolver/source_variable_test.cc b/src/tint/resolver/source_variable_test.cc
index 2648e93..f80c980 100644
--- a/src/tint/resolver/source_variable_test.cc
+++ b/src/tint/resolver/source_variable_test.cc
@@ -128,7 +128,7 @@
 TEST_F(ResolverSourceVariableTest, Parameter) {
     auto* a = Param("a", ty.f32());
     auto* expr = Expr(a);
-    Func("foo", {a}, ty.void_(), {WrapInStatement(expr)});
+    Func("foo", utils::Vector{a}, ty.void_(), utils::Vector{WrapInStatement(expr)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -145,7 +145,8 @@
     auto* expr_param = Expr(param);
     auto* let = Let("b", nullptr, expr_param);
     auto* expr_let = Expr("b");
-    Func("foo", {param}, ty.void_(), {WrapInStatement(let), WrapInStatement(expr_let)});
+    Func("foo", utils::Vector{param}, ty.void_(),
+         utils::Vector{WrapInStatement(let), WrapInStatement(expr_let)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -213,7 +214,7 @@
     // {
     //   a.f
     // }
-    auto* S = Structure("S", {Member("f", ty.f32())});
+    auto* S = Structure("S", utils::Vector{Member("f", ty.f32())});
     auto* a = GlobalVar("a", ty.Of(S), ast::StorageClass::kPrivate);
     auto* expr = MemberAccessor(a, "f");
     WrapInFunction(expr);
diff --git a/src/tint/resolver/storage_class_layout_validation_test.cc b/src/tint/resolver/storage_class_layout_validation_test.cc
index 6952cb5..1f06d89 100644
--- a/src/tint/resolver/storage_class_layout_validation_test.cc
+++ b/src/tint/resolver/storage_class_layout_validation_test.cc
@@ -34,8 +34,10 @@
     // var<storage> a : S;
 
     Structure(Source{{12, 34}}, "S",
-              {Member("a", ty.f32(), {MemberSize(5)}),
-               Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(1)})});
+              utils::Vector{
+                  Member("a", ty.f32(), utils::Vector{MemberSize(5)}),
+                  Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(1)}),
+              });
 
     GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
               GroupAndBinding(0, 0));
@@ -62,8 +64,10 @@
     // var<storage> a : S;
 
     Structure(Source{{12, 34}}, "S",
-              {Member("a", ty.f32(), {MemberSize(5)}),
-               Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(4)})});
+              utils::Vector{
+                  Member("a", ty.f32(), utils::Vector{MemberSize(5)}),
+                  Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(4)}),
+              });
 
     GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
               GroupAndBinding(0, 0));
@@ -85,10 +89,13 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner", {Member("scalar", ty.i32())});
+    Structure(Source{{12, 34}}, "Inner",
+              utils::Vector{
+                  Member("scalar", ty.i32()),
+              });
 
     Structure(Source{{34, 56}}, "Outer",
-              {
+              utils::Vector{
                   Member("scalar", ty.f32()),
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
               });
@@ -126,12 +133,16 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    Structure(Source{{12, 34}}, "Inner", {Member("scalar", ty.i32())});
+    Structure(Source{{12, 34}}, "Inner",
+              utils::Vector{
+                  Member("scalar", ty.i32()),
+              });
 
     Structure(Source{{34, 56}}, "Outer",
-              {
+              utils::Vector{
                   Member("scalar", ty.f32()),
-                  Member(Source{{56, 78}}, "inner", ty.type_name("Inner"), {MemberAlign(16)}),
+                  Member(Source{{56, 78}}, "inner", ty.type_name("Inner"),
+                         utils::Vector{MemberAlign(16)}),
               });
 
     GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
@@ -154,7 +165,7 @@
     Alias("Inner", ty.array(ty.f32(), 10_u, 16));
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("scalar", ty.f32()),
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
               });
@@ -187,9 +198,10 @@
     Alias("Inner", ty.array(ty.f32(), 10_u, 16));
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("scalar", ty.f32()),
-                  Member(Source{{34, 56}}, "inner", ty.type_name("Inner"), {MemberAlign(16)}),
+                  Member(Source{{34, 56}}, "inner", ty.type_name("Inner"),
+                         utils::Vector{MemberAlign(16)}),
               });
 
     GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
@@ -214,10 +226,12 @@
     // var<uniform> a : Outer;
 
     Structure(Source{{12, 34}}, "Inner",
-              {Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)})});
+              utils::Vector{
+                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1), MemberSize(5)}),
+              });
 
     Structure(Source{{34, 56}}, "Outer",
-              {
+              utils::Vector{
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
                   Member(Source{{78, 90}}, "scalar", ty.i32()),
               });
@@ -261,15 +275,15 @@
     // var<uniform> a : Outer;
 
     Structure(Source{{12, 34}}, "Inner",
-              {
+              utils::Vector{
                   Member("a", ty.i32()),
                   Member("b", ty.i32()),
                   Member("c", ty.i32()),
-                  Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)}),
+                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1), MemberSize(5)}),
               });
 
     Structure(Source{{34, 56}}, "Outer",
-              {
+              utils::Vector{
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
                   Member(Source{{78, 90}}, "scalar", ty.i32()),
               });
@@ -312,12 +326,14 @@
     // var<uniform> a : Outer;
 
     Structure(Source{{12, 34}}, "Inner",
-              {Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)})});
+              utils::Vector{
+                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1), MemberSize(5)}),
+              });
 
     Structure(Source{{34, 56}}, "Outer",
-              {
+              utils::Vector{
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
-                  Member(Source{{78, 90}}, "scalar", ty.i32(), {MemberAlign(16)}),
+                  Member(Source{{78, 90}}, "scalar", ty.i32(), utils::Vector{MemberAlign(16)}),
               });
 
     GlobalVar(Source{{22, 34}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
@@ -336,7 +352,7 @@
     // @group(0) @binding(0)
     // var<uniform> a : ScalarPackedAtEndOfVec3;
 
-    Structure("ScalarPackedAtEndOfVec3", {
+    Structure("ScalarPackedAtEndOfVec3", utils::Vector{
                                              Member("v", ty.vec3(ty.f32())),
                                              Member("s", ty.f32()),
                                          });
@@ -362,7 +378,7 @@
     Alias("Inner", ty.array(ty.f32(), 10_u));
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                   Member("scalar", ty.i32()),
               });
@@ -396,7 +412,7 @@
     Alias("Inner", ty.array(ty.vec2<f32>(), 10_u));
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                   Member("scalar", ty.i32()),
               });
@@ -432,14 +448,14 @@
     // @group(0) @binding(0)
     // var<uniform> a : Outer;
 
-    auto* array_elem = Structure("ArrayElem", {
+    auto* array_elem = Structure("ArrayElem", utils::Vector{
                                                   Member("a", ty.f32()),
                                                   Member("b", ty.i32()),
                                               });
     Alias("Inner", ty.array(ty.Of(array_elem), 10_u));
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                   Member("scalar", ty.i32()),
               });
@@ -480,7 +496,7 @@
     // var<uniform> a : array<Outer, 4u>;
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("inner", ty.array(Source{{34, 56}}, ty.array(ty.f32(), 4_u), 4_u)),
               });
 
@@ -512,7 +528,7 @@
     Alias("Inner", ty.array(ty.f32(), 10_u, 16));
 
     Structure(Source{{12, 34}}, "Outer",
-              {
+              utils::Vector{
                   Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
                   Member("scalar", ty.i32()),
               });
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/storage_class_validation_test.cc
index 5e6e9ce..355bdb9 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/storage_class_validation_test.cc
@@ -55,7 +55,7 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArrayInStruct) {
-    auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.array(ty.i32()))});
     GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -75,7 +75,7 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArrayInStruct) {
-    auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
+    auto* s = Structure("S", utils::Vector{Member("m", ty.array(ty.i32()))});
     GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
@@ -88,7 +88,7 @@
 TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
     // var<storage> g : bool;
     GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -106,7 +106,7 @@
     // var<storage, read> g : a;
     auto* a = Alias("a", ty.bool_());
     GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -126,7 +126,7 @@
     Enable(ast::Extension::kF16);
 
     GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -145,7 +145,7 @@
 
     auto* a = Alias("a", ty.f16());
     GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -161,7 +161,7 @@
     // var<storage> g : vec4<f16>;
     Enable(ast::Extension::kF16);
     GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -178,10 +178,10 @@
     // var<storage, read> g : array<S, 3u>;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {Member("a", ty.f16(Source{{56, 78}}))});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f16(Source{{56, 78}}))});
     auto* a = ty.array(ty.Of(s), 3_u);
     GlobalVar("g", a, ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -197,9 +197,9 @@
     // var<storage, read> g : S;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
     GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -216,11 +216,11 @@
     // var<storage, read> g : a1;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
     auto* a1 = Alias("a1", ty.Of(s));
     auto* a2 = Alias("a2", ty.Of(a1));
     GlobalVar("g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -235,7 +235,7 @@
     // var<storage> g : ptr<private, f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
               ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -251,7 +251,7 @@
 TEST_F(ResolverStorageClassValidationTest, StorageBufferIntScalar) {
     // var<storage> g : i32;
     GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -262,7 +262,7 @@
 TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF32) {
     // var<storage> g : vec4<f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -272,10 +272,10 @@
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF32) {
     // var<storage, read> g : array<S, 3u>;
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
     GlobalVar(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -324,9 +324,9 @@
 TEST_F(ResolverStorageClassValidationTest, StorageBufferStructI32) {
     // struct S { x : i32 };
     // var<storage, read> g : S;
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -338,11 +338,11 @@
     // struct S { x : i32 };
     // type a1 = S;
     // var<storage, read> g : a1;
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     auto* a1 = Alias("a1", ty.Of(s));
     auto* a2 = Alias("a2", ty.Of(a1));
     GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -354,10 +354,10 @@
     // struct S { m:  array<f32>; };
     // @group(0) @binding(0) var<uniform, > svar : S;
 
-    auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())});
+    auto* s = Structure(Source{{12, 34}}, "S", utils::Vector{Member("m", ty.array<i32>())});
 
     GlobalVar(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -372,7 +372,7 @@
 TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
     // var<uniform> g : bool;
     GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -390,7 +390,7 @@
     // var<uniform> g : a;
     auto* a = Alias("a", ty.bool_());
     GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -410,7 +410,7 @@
     Enable(ast::Extension::kF16);
 
     GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -429,7 +429,7 @@
 
     auto* a = Alias("a", ty.f16());
     GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -445,7 +445,7 @@
     // var<uniform> g : vec4<f16>;
     Enable(ast::Extension::kF16);
     GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -463,10 +463,11 @@
     // var<uniform> g : array<S, 3u>;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {Member("a", ty.f16(Source{{56, 78}}), {MemberSize(16)})});
+    auto* s = Structure(
+        "S", utils::Vector{Member("a", ty.f16(Source{{56, 78}}), utils::Vector{MemberSize(16)})});
     auto* a = ty.array(ty.Of(s), 3_u);
     GlobalVar("g", a, ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -482,9 +483,9 @@
     // var<uniform> g :  S;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
     GlobalVar("g", ty.Of(s), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -501,10 +502,10 @@
     // var<uniform> g : a1;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
+    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
     auto* a1 = Alias("a1", ty.Of(s));
     GlobalVar("g", ty.Of(a1), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -519,7 +520,7 @@
     // var<uniform> g : ptr<private, f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
               ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -535,7 +536,7 @@
 TEST_F(ResolverStorageClassValidationTest, UniformBufferIntScalar) {
     // var<uniform> g : i32;
     GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -546,7 +547,7 @@
 TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF32) {
     // var<uniform> g : vec4<f32>;
     GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -559,10 +560,10 @@
     //   @size(16) f : f32;
     // }
     // var<uniform> g : array<S, 3u>;
-    auto* s = Structure("S", {Member("a", ty.f32(), {MemberSize(16)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16)})});
     auto* a = ty.array(ty.Of(s), 3_u);
     GlobalVar(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -573,9 +574,9 @@
 TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32) {
     // struct S { x : i32 };
     // var<uniform> g :  S;
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -587,10 +588,10 @@
     // struct S { x : i32 };
     // type a1 = S;
     // var<uniform> g : a1;
-    auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     auto* a1 = Alias("a1", ty.Of(s));
     GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
diff --git a/src/tint/resolver/struct_layout_test.cc b/src/tint/resolver/struct_layout_test.cc
index 73ecd4e..59faf8d 100644
--- a/src/tint/resolver/struct_layout_test.cc
+++ b/src/tint/resolver/struct_layout_test.cc
@@ -26,7 +26,7 @@
 using ResolverStructLayoutTest = ResolverTest;
 
 TEST_F(ResolverStructLayoutTest, Scalars) {
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.f32()),
                                  Member("b", ty.u32()),
                                  Member("c", ty.i32()),
@@ -57,7 +57,7 @@
 TEST_F(ResolverStructLayoutTest, ScalarsWithF16) {
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.f32()),
                                  Member("b", ty.f16()),
                                  Member("c", ty.u32()),
@@ -109,7 +109,7 @@
     auto* alias_a = Alias("a", ty.f32());
     auto* alias_b = Alias("b", ty.f32());
 
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.Of(alias_a)),
                                  Member("b", ty.Of(alias_b)),
                              });
@@ -136,7 +136,7 @@
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) {
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.array<i32, 3>()),
                                  Member("b", ty.array<f32, 5>()),
                                  Member("c", ty.array<f16, 7>()),
@@ -176,7 +176,7 @@
 TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) {
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.array<i32, 3>(/*stride*/ 8)),
                                  Member("b", ty.array<f32, 5>(/*stride*/ 16)),
                                  Member("c", ty.array<f16, 7>(/*stride*/ 4)),
@@ -214,7 +214,7 @@
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("c", ty.array<f32>()),
                              });
 
@@ -235,7 +235,7 @@
 }
 
 TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("c", ty.array<f32>(/*stride*/ 32)),
                              });
 
@@ -258,7 +258,7 @@
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
     auto* inner = ty.array<i32, 2>(/*stride*/ 16);  // size: 32
     auto* outer = ty.array(inner, 12_u);            // size: 12 * 32
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("c", outer),
                              });
 
@@ -279,13 +279,13 @@
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
-    auto* inner = Structure("Inner", {
+    auto* inner = Structure("Inner", utils::Vector{
                                          Member("a", ty.vec2<i32>()),
                                          Member("b", ty.vec3<i32>()),
                                          Member("c", ty.vec4<i32>()),
                                      });         // size: 48
     auto* outer = ty.array(ty.Of(inner), 12_u);  // size: 12 * 48
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("c", outer),
                              });
 
@@ -306,7 +306,7 @@
 }
 
 TEST_F(ResolverStructLayoutTest, Vector) {
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.vec2<i32>()),
                                  Member("b", ty.vec3<i32>()),
                                  Member("c", ty.vec4<i32>()),
@@ -337,7 +337,7 @@
 TEST_F(ResolverStructLayoutTest, Matrix) {
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a_1", ty.mat2x2<f32>()),
                                  Member("a_2", ty.mat2x2<f16>()),
                                  Member("b_1", ty.mat2x3<f32>()),
@@ -427,10 +427,10 @@
 }
 
 TEST_F(ResolverStructLayoutTest, NestedStruct) {
-    auto* inner = Structure("Inner", {
+    auto* inner = Structure("Inner", utils::Vector{
                                          Member("a", ty.mat3x3<f32>()),
                                      });
-    auto* s = Structure("S", {
+    auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.i32()),
                                  Member("b", ty.Of(inner)),
                                  Member("c", ty.i32()),
@@ -459,16 +459,16 @@
 }
 
 TEST_F(ResolverStructLayoutTest, SizeAttributes) {
-    auto* inner = Structure("Inner", {
-                                         Member("a", ty.f32(), {MemberSize(8)}),
-                                         Member("b", ty.f32(), {MemberSize(16)}),
-                                         Member("c", ty.f32(), {MemberSize(8)}),
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.f32(), utils::Vector{MemberSize(8)}),
+                                         Member("b", ty.f32(), utils::Vector{MemberSize(16)}),
+                                         Member("c", ty.f32(), utils::Vector{MemberSize(8)}),
                                      });
-    auto* s = Structure("S", {
-                                 Member("a", ty.f32(), {MemberSize(4)}),
-                                 Member("b", ty.u32(), {MemberSize(8)}),
+    auto* s = Structure("S", utils::Vector{
+                                 Member("a", ty.f32(), utils::Vector{MemberSize(4)}),
+                                 Member("b", ty.u32(), utils::Vector{MemberSize(8)}),
                                  Member("c", ty.Of(inner)),
-                                 Member("d", ty.i32(), {MemberSize(32)}),
+                                 Member("d", ty.i32(), utils::Vector{MemberSize(32)}),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -497,16 +497,16 @@
 }
 
 TEST_F(ResolverStructLayoutTest, AlignAttributes) {
-    auto* inner = Structure("Inner", {
-                                         Member("a", ty.f32(), {MemberAlign(8)}),
-                                         Member("b", ty.f32(), {MemberAlign(16)}),
-                                         Member("c", ty.f32(), {MemberAlign(4)}),
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("a", ty.f32(), utils::Vector{MemberAlign(8)}),
+                                         Member("b", ty.f32(), utils::Vector{MemberAlign(16)}),
+                                         Member("c", ty.f32(), utils::Vector{MemberAlign(4)}),
                                      });
-    auto* s = Structure("S", {
-                                 Member("a", ty.f32(), {MemberAlign(4)}),
-                                 Member("b", ty.u32(), {MemberAlign(8)}),
+    auto* s = Structure("S", utils::Vector{
+                                 Member("a", ty.f32(), utils::Vector{MemberAlign(4)}),
+                                 Member("b", ty.u32(), utils::Vector{MemberAlign(8)}),
                                  Member("c", ty.Of(inner)),
-                                 Member("d", ty.i32(), {MemberAlign(32)}),
+                                 Member("d", ty.i32(), utils::Vector{MemberAlign(32)}),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -535,8 +535,8 @@
 }
 
 TEST_F(ResolverStructLayoutTest, StructWithLotsOfPadding) {
-    auto* s = Structure("S", {
-                                 Member("a", ty.i32(), {MemberAlign(1024)}),
+    auto* s = Structure("S", utils::Vector{
+                                 Member("a", ty.i32(), utils::Vector{MemberAlign(1024)}),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/struct_pipeline_stage_use_test.cc b/src/tint/resolver/struct_pipeline_stage_use_test.cc
index 58c7cae..acf30f6 100644
--- a/src/tint/resolver/struct_pipeline_stage_use_test.cc
+++ b/src/tint/resolver/struct_pipeline_stage_use_test.cc
@@ -29,7 +29,7 @@
 using ResolverPipelineStageUseTest = ResolverTest;
 
 TEST_F(ResolverPipelineStageUseTest, UnusedStruct) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -39,9 +39,9 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointParam) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
 
-    Func("foo", {Param("param", ty.Of(s))}, ty.void_(), {}, {});
+    Func("foo", utils::Vector{Param("param", ty.Of(s))}, ty.void_(), utils::Empty, utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -51,9 +51,10 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointReturnType) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
 
-    Func("foo", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0_f)))}, {});
+    Func("foo", utils::Empty, ty.Of(s), utils::Vector{Return(Construct(ty.Of(s), Expr(0_f)))},
+         utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -63,10 +64,12 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
 
-    Func("main", {Param("param", ty.Of(s))}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
-         {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::BuiltinValue::kPosition)});
+    Func("main", utils::Vector{Param("param", ty.Of(s))}, ty.vec4<f32>(),
+         utils::Vector{Return(Construct(ty.vec4<f32>()))},
+         utils::Vector{Stage(ast::PipelineStage::kVertex)},
+         utils::Vector{Builtin(ast::BuiltinValue::kPosition)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -78,9 +81,11 @@
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
     auto* s =
-        Structure("S", {Member("a", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)})});
+        Structure("S", utils::Vector{Member("a", ty.vec4<f32>(),
+                                            utils::Vector{Builtin(ast::BuiltinValue::kPosition)})});
 
-    Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))}, {Stage(ast::PipelineStage::kVertex)});
+    Func("main", utils::Empty, ty.Of(s), utils::Vector{Return(Construct(ty.Of(s)))},
+         utils::Vector{Stage(ast::PipelineStage::kVertex)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -91,10 +96,10 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderParam) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
 
-    Func("main", {Param("param", ty.Of(s))}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("main", utils::Vector{Param("param", ty.Of(s))}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -105,10 +110,10 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderReturnType) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
 
-    Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0_f)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("main", utils::Empty, ty.Of(s), utils::Vector{Return(Construct(ty.Of(s), Expr(0_f)))},
+         utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -120,10 +125,11 @@
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
     auto* s = Structure(
-        "S", {Member("a", ty.u32(), {Builtin(ast::BuiltinValue::kLocalInvocationIndex)})});
+        "S", utils::Vector{Member(
+                 "a", ty.u32(), utils::Vector{Builtin(ast::BuiltinValue::kLocalInvocationIndex)})});
 
-    Func("main", {Param("param", ty.Of(s))}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
+    Func("main", utils::Vector{Param("param", ty.Of(s))}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -135,13 +141,14 @@
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
     auto* s =
-        Structure("S", {Member("a", ty.vec4<f32>(), {Builtin(ast::BuiltinValue::kPosition)})});
+        Structure("S", utils::Vector{Member("a", ty.vec4<f32>(),
+                                            utils::Vector{Builtin(ast::BuiltinValue::kPosition)})});
 
-    Func("vert_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
-         {Stage(ast::PipelineStage::kVertex)});
+    Func("vert_main", utils::Empty, ty.Of(s), utils::Vector{Return(Construct(ty.Of(s)))},
+         utils::Vector{Stage(ast::PipelineStage::kVertex)});
 
-    Func("frag_main", {Param("param", ty.Of(s))}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("frag_main", utils::Vector{Param("param", ty.Of(s))}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -153,11 +160,11 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
     auto* s_alias = Alias("S_alias", ty.Of(s));
 
-    Func("main", {Param("param", ty.Of(s_alias))}, ty.void_(), {},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("main", utils::Vector{Param("param", ty.Of(s_alias))}, ty.void_(), utils::Empty,
+         utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -168,11 +175,12 @@
 }
 
 TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
-    auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{Location(0)})});
     auto* s_alias = Alias("S_alias", ty.Of(s));
 
-    Func("main", {}, ty.Of(s_alias), {Return(Construct(ty.Of(s_alias), Expr(0_f)))},
-         {Stage(ast::PipelineStage::kFragment)});
+    Func("main", utils::Empty, ty.Of(s_alias),
+         utils::Vector{Return(Construct(ty.Of(s_alias), Expr(0_f)))},
+         utils::Vector{Stage(ast::PipelineStage::kFragment)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/struct_storage_class_use_test.cc b/src/tint/resolver/struct_storage_class_use_test.cc
index 5a929a5..d58c906 100644
--- a/src/tint/resolver/struct_storage_class_use_test.cc
+++ b/src/tint/resolver/struct_storage_class_use_test.cc
@@ -28,7 +28,7 @@
 using ResolverStorageClassUseTest = ResolverTest;
 
 TEST_F(ResolverStorageClassUseTest, UnreachableStruct) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -38,9 +38,9 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableFromParameter) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
-    Func("f", {Param("param", ty.Of(s))}, ty.void_(), {}, {});
+    Func("f", utils::Vector{Param("param", ty.Of(s))}, ty.void_(), utils::Empty, utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -50,9 +50,9 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableFromReturnType) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
-    Func("f", {}, ty.Of(s), {Return(Construct(ty.Of(s)))}, {});
+    Func("f", utils::Empty, ty.Of(s), utils::Vector{Return(Construct(ty.Of(s)))}, utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -62,7 +62,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableFromGlobal) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
@@ -74,7 +74,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalAlias) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = Alias("A", ty.Of(s));
     GlobalVar("g", ty.Of(a), ast::StorageClass::kPrivate);
 
@@ -86,8 +86,8 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalStruct) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
-    auto* o = Structure("O", {Member("a", ty.Of(s))});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
+    auto* o = Structure("O", utils::Vector{Member("a", ty.Of(s))});
     GlobalVar("g", ty.Of(o), ast::StorageClass::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -98,7 +98,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
     GlobalVar("g", a, ast::StorageClass::kPrivate);
 
@@ -110,7 +110,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableFromLocal) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     WrapInFunction(Var("g", ty.Of(s)));
 
@@ -122,7 +122,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalAlias) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = Alias("A", ty.Of(s));
     WrapInFunction(Var("g", ty.Of(a)));
 
@@ -134,8 +134,8 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalStruct) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
-    auto* o = Structure("O", {Member("a", ty.Of(s))});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
+    auto* o = Structure("O", utils::Vector{Member("a", ty.Of(s))});
     WrapInFunction(Var("g", ty.Of(o)));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -146,7 +146,7 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
     WrapInFunction(Var("g", a));
 
@@ -158,14 +158,14 @@
 }
 
 TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
-    auto* s = Structure("S", {Member("a", ty.f32())});
+    auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     GlobalVar("x", ty.Of(s), ast::StorageClass::kUniform,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
     GlobalVar("y", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(1u),
                   create<ast::GroupAttribute>(0u),
               });
diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc
index fe1973b..33c15f3 100644
--- a/src/tint/resolver/type_constructor_validation_test.cc
+++ b/src/tint/resolver/type_constructor_validation_test.cc
@@ -186,8 +186,8 @@
 
     Enable(ast::Extension::kF16);
 
-    Func("foo", {}, params.create_rhs_ast_type(*this),
-         {Return(Construct(params.create_rhs_ast_type(*this)))}, {});
+    Func("foo", utils::Empty, params.create_rhs_ast_type(*this),
+         utils::Vector{Return(Construct(params.create_rhs_ast_type(*this)))}, {});
 
     auto* a = Var("a", nullptr, Call("foo"));
     // Self-assign 'a' to force the expression to be resolved so we can test its
@@ -2474,10 +2474,10 @@
 
     const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns - 1; i++) {
         auto* vec_type = param.create_column_ast_type(*this);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2503,9 +2503,9 @@
 
     const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns * param.rows - 1; i++) {
-        args.push_back(Construct(param.create_element_ast_type(*this)));
+        args.Push(Construct(param.create_element_ast_type(*this)));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2531,10 +2531,10 @@
 
     const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns + 1; i++) {
         auto* vec_type = param.create_column_ast_type(*this);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2560,9 +2560,9 @@
 
     const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns * param.rows + 1; i++) {
-        args.push_back(Construct(param.create_element_ast_type(*this)));
+        args.Push(Construct(param.create_element_ast_type(*this)));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2587,10 +2587,10 @@
     Enable(ast::Extension::kF16);
 
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec<u32>(param.rows);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2615,9 +2615,9 @@
     Enable(ast::Extension::kF16);
 
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        args.push_back(Expr(1_u));
+        args.Push(Expr(1_u));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2648,10 +2648,10 @@
 
     const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* valid_vec_type = param.create_column_ast_type(*this);
-        args.push_back(Construct(valid_vec_type));
+        args.Push(Construct(valid_vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2659,7 +2659,7 @@
     }
     const size_t kInvalidLoc = 2 * (param.columns - 1);
     auto* invalid_vec_type = ty.vec(param.create_element_ast_type(*this), param.rows - 1);
-    args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
+    args.Push(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
     args_tys << ", vec" << (param.rows - 1) << "<" + element_type_name + ">";
 
     auto* matrix_type = param.create_mat_ast_type(*this);
@@ -2686,17 +2686,17 @@
 
     const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* valid_vec_type = param.create_column_ast_type(*this);
-        args.push_back(Construct(valid_vec_type));
+        args.Push(Construct(valid_vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
         args_tys << "vec" << param.rows << "<" + element_type_name + ">";
     }
     auto* invalid_vec_type = ty.vec(param.create_element_ast_type(*this), param.rows + 1);
-    args.push_back(Construct(invalid_vec_type));
+    args.Push(Construct(invalid_vec_type));
     args_tys << ", vec" << (param.rows + 1) << "<" + element_type_name + ">";
 
     auto* matrix_type = param.create_mat_ast_type(*this);
@@ -2731,10 +2731,10 @@
 
     Enable(ast::Extension::kF16);
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 4> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = param.create_column_ast_type(*this);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
     }
 
     auto* matrix_type = param.create_mat_ast_type(*this);
@@ -2752,9 +2752,9 @@
 
     Enable(ast::Extension::kF16);
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 16> args;
     for (uint32_t i = 0; i < param.columns * param.rows; i++) {
-        args.push_back(Construct(param.create_element_ast_type(*this)));
+        args.Push(Construct(param.create_element_ast_type(*this)));
     }
 
     auto* matrix_type = param.create_mat_ast_type(*this);
@@ -2775,10 +2775,10 @@
     auto* elem_type_alias = Alias("ElemType", param.create_element_ast_type(*this));
 
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 4> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec(ty.u32(), param.rows);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2804,10 +2804,10 @@
 
     auto* elem_type_alias = Alias("ElemType", param.create_element_ast_type(*this));
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = param.create_column_ast_type(*this);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
     }
 
     auto* matrix_type = ty.mat(ty.Of(elem_type_alias), param.columns, param.rows);
@@ -2837,9 +2837,9 @@
     auto* vec_type = param.create_column_ast_type(*this);
     auto* vec_alias = Alias("ColVectorAlias", vec_type);
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 4> args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        args.push_back(Construct(ty.Of(vec_alias)));
+        args.Push(Construct(ty.Of(vec_alias)));
     }
 
     auto* tc = Construct(Source{}, matrix_type, std::move(args));
@@ -2857,10 +2857,10 @@
     auto* u32_type_alias = Alias("UnsignedInt", ty.u32());
 
     std::stringstream args_tys;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 4> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec(ty.Of(u32_type_alias), param.rows);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
@@ -2882,10 +2882,10 @@
 
     auto* elem_type_alias = Alias("ElemType", param.create_element_ast_type(*this));
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 4> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         auto* vec_type = ty.vec(ty.Of(elem_type_alias), param.rows);
-        args.push_back(Construct(vec_type));
+        args.Push(Construct(vec_type));
     }
 
     auto* matrix_type = param.create_mat_ast_type(*this);
@@ -2900,9 +2900,9 @@
 
     Enable(ast::Extension::kF16);
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        args.push_back(Construct(param.create_column_ast_type(*this)));
+        args.Push(Construct(param.create_column_ast_type(*this)));
     }
 
     auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
@@ -2917,9 +2917,9 @@
 
     Enable(ast::Extension::kF16);
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.rows * param.columns; i++) {
-        args.push_back(param.create_element_ast_value(*this, static_cast<double>(i)));
+        args.Push(param.create_element_ast_value(*this, static_cast<double>(i)));
     }
 
     auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
@@ -2937,17 +2937,17 @@
     err << "12:34 error: no matching constructor for mat" << param.columns << "x" << param.rows
         << "(";
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 8> args;
     for (uint32_t i = 0; i < param.columns; i++) {
         if (i > 0) {
             err << ", ";
         }
         if (i == 1) {
             // Odd one out
-            args.push_back(Construct(ty.vec<i32>(param.rows)));
+            args.Push(Construct(ty.vec<i32>(param.rows)));
             err << "vec" << param.rows << "<i32>";
         } else {
-            args.push_back(Construct(param.create_column_ast_type(*this)));
+            args.Push(Construct(param.create_column_ast_type(*this)));
             err << "vec" << param.rows << "<" + param.get_element_type_name() + ">";
         }
     }
@@ -2968,16 +2968,16 @@
     err << "12:34 error: no matching constructor for mat" << param.columns << "x" << param.rows
         << "(";
 
-    ast::ExpressionList args;
+    utils::Vector<const ast::Expression*, 16> args;
     for (uint32_t i = 0; i < param.rows * param.columns; i++) {
         if (i > 0) {
             err << ", ";
         }
         if (i == 3) {
-            args.push_back(Expr(static_cast<i32>(i)));  // The odd one out
+            args.Push(Expr(static_cast<i32>(i)));  // The odd one out
             err << "i32";
         } else {
-            args.push_back(param.create_element_ast_value(*this, static_cast<double>(i)));
+            args.Push(param.create_element_ast_value(*this, static_cast<double>(i)));
             err << param.get_element_type_name();
         }
     }
@@ -3054,14 +3054,14 @@
 
     Enable(ast::Extension::kF16);
 
-    ast::StructMemberList members;
-    ast::ExpressionList values;
+    utils::Vector<const ast::StructMember*, 16> members;
+    utils::Vector<const ast::Expression*, 16> values;
     for (uint32_t i = 0; i < N; i++) {
         auto* struct_type = str_params.ast(*this);
-        members.push_back(Member("member_" + std::to_string(i), struct_type));
+        members.Push(Member("member_" + std::to_string(i), struct_type));
         if (i < N - 1) {
             auto* ctor_value_expr = str_params.expr(*this, 0);
-            values.push_back(ctor_value_expr);
+            values.Push(ctor_value_expr);
         }
     }
     auto* s = Structure("s", members);
@@ -3079,15 +3079,15 @@
 
     Enable(ast::Extension::kF16);
 
-    ast::StructMemberList members;
-    ast::ExpressionList values;
+    utils::Vector<const ast::StructMember*, 16> members;
+    utils::Vector<const ast::Expression*, 8> values;
     for (uint32_t i = 0; i < N + 1; i++) {
         if (i < N) {
             auto* struct_type = str_params.ast(*this);
-            members.push_back(Member("member_" + std::to_string(i), struct_type));
+            members.Push(Member("member_" + std::to_string(i), struct_type));
         }
         auto* ctor_value_expr = str_params.expr(*this, 0);
-        values.push_back(ctor_value_expr);
+        values.Push(ctor_value_expr);
     }
     auto* s = Structure("s", members);
     auto* tc = Construct(Source{{12, 34}}, ty.Of(s), values);
@@ -3116,17 +3116,17 @@
         return;
     }
 
-    ast::StructMemberList members;
-    ast::ExpressionList values;
+    utils::Vector<const ast::StructMember*, 16> members;
+    utils::Vector<const ast::Expression*, 8> values;
     // make the last value of the constructor to have a different type
     uint32_t constructor_value_with_different_type = N - 1;
     for (uint32_t i = 0; i < N; i++) {
         auto* struct_type = str_params.ast(*this);
-        members.push_back(Member("member_" + std::to_string(i), struct_type));
+        members.Push(Member("member_" + std::to_string(i), struct_type));
         auto* ctor_value_expr = (i == constructor_value_with_different_type)
                                     ? ctor_params.expr(*this, 0)
                                     : str_params.expr(*this, 0);
-        values.push_back(ctor_value_expr);
+        values.Push(ctor_value_expr);
     }
     auto* s = Structure("s", members);
     auto* tc = Construct(ty.Of(s), values);
@@ -3149,12 +3149,12 @@
 
 TEST_F(ResolverTypeConstructorValidationTest, Struct_Nested) {
     auto* inner_m = Member("m", ty.i32());
-    auto* inner_s = Structure("inner_s", {inner_m});
+    auto* inner_s = Structure("inner_s", utils::Vector{inner_m});
 
     auto* m0 = Member("m0", ty.i32());
     auto* m1 = Member("m1", ty.Of(inner_s));
     auto* m2 = Member("m2", ty.i32());
-    auto* s = Structure("s", {m0, m1, m2});
+    auto* s = Structure("s", utils::Vector{m0, m1, m2});
 
     auto* tc = Construct(Source{{12, 34}}, ty.Of(s), 1_i, 1_i, 1_i);
     WrapInFunction(tc);
@@ -3166,14 +3166,14 @@
 
 TEST_F(ResolverTypeConstructorValidationTest, Struct) {
     auto* m = Member("m", ty.i32());
-    auto* s = Structure("MyInputs", {m});
+    auto* s = Structure("MyInputs", utils::Vector{m});
     auto* tc = Construct(Source{{12, 34}}, ty.Of(s));
     WrapInFunction(tc);
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Struct_Empty) {
-    auto* str = Structure("S", {
+    auto* str = Structure("S", utils::Vector{
                                    Member("a", ty.i32()),
                                    Member("b", ty.f32()),
                                    Member("c", ty.vec3<i32>()),
@@ -3200,7 +3200,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, NonConstructibleType_AtomicStructMember) {
-    auto* str = Structure("S", {Member("a", ty.atomic(ty.i32()))});
+    auto* str = Structure("S", utils::Vector{Member("a", ty.atomic(ty.i32()))});
     WrapInFunction(Assign(Phony(), Construct(Source{{12, 34}}, ty.Of(str))));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index e746e51..d7c2e64 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -75,7 +75,10 @@
 
 TEST_F(ResolverTypeValidationTest, GlobalOverrideNoConstructor_Pass) {
     // @id(0) override a :i32;
-    Override(Source{{12, 34}}, "a", ty.i32(), nullptr, ast::AttributeList{Id(0)});
+    Override(Source{{12, 34}}, "a", ty.i32(), nullptr,
+             utils::Vector{
+                 Id(0),
+             });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -111,8 +114,8 @@
     // }
     // var a: f32 = 2.1;
 
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2_f))),
          });
 
@@ -163,14 +166,14 @@
 
     auto* var1 = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(1_f));
 
-    Func("func0", {}, ty.void_(),
-         {
+    Func("func0", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Source{{12, 34}}, var0),
              Return(),
          });
 
-    Func("func1", {}, ty.void_(),
-         {
+    Func("func1", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(Source{{13, 34}}, var1),
              Return(),
          });
@@ -383,11 +386,11 @@
 
     auto* var = Var(Source{{12, 34}}, "a", ty.array<i32>(), ast::StorageClass::kNone);
 
-    Func("func", {}, ty.void_(),
-         {
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          });
 
@@ -402,7 +405,9 @@
     //   a: vec3;
     // };
 
-    Structure("S", {Member("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u))});
+    Structure("S", utils::Vector{
+                       Member("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u)),
+                   });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
@@ -412,7 +417,9 @@
     // struct S {
     //   a: mat3x3;
     // };
-    Structure("S", {Member("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u))});
+    Structure("S", utils::Vector{
+                       Member("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u)),
+                   });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
@@ -425,13 +432,11 @@
     // };
 
     Structure(Source{{12, 34}}, "Foo",
-              {
+              utils::Vector{
                   Member("a", ty.array<f32, 0x20000000>()),
                   Member("b", ty.array<f32, 0x20000000>()),
               });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: struct size (0x100000000) must not exceed 0xffffffff bytes");
@@ -444,14 +449,12 @@
     //   c: f32;
     // };
 
-    Structure("Foo", {
+    Structure("Foo", utils::Vector{
                          Member("a", ty.array<f32, 0x3fffffff>()),
                          Member("b", ty.f32()),
                          Member(Source{{12, 34}}, "c", ty.f32()),
                      });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: struct member offset (0x100000000) must not exceed 0xffffffff bytes");
@@ -463,13 +466,11 @@
     //   rt: array<f32>;
     // };
 
-    Structure("Foo", {
+    Structure("Foo", utils::Vector{
                          Member("vf", ty.f32()),
                          Member("rt", ty.array<f32>()),
                      });
 
-    WrapInFunction();
-
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -478,7 +479,9 @@
     //   rt : array<array<f32>, 4u>;
     // };
 
-    Structure("Foo", {Member("rt", ty.array(Source{{12, 34}}, ty.array<f32>(), 4_u))});
+    Structure("Foo", utils::Vector{
+                         Member("rt", ty.array(Source{{12, 34}}, ty.array<f32>(), 4_u)),
+                     });
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
@@ -491,7 +494,9 @@
     // };
     // var<private> a : array<Foo, 4>;
 
-    auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
+    auto* foo = Structure("Foo", utils::Vector{
+                                     Member("rt", ty.array<f32>()),
+                                 });
     GlobalVar("v", ty.array(Source{{12, 34}}, ty.Of(foo), 4_u), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
@@ -507,8 +512,12 @@
     //   inner : Foo;
     // };
 
-    auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
-    Structure("Outer", {Member(Source{{12, 34}}, "inner", ty.Of(foo))});
+    auto* foo = Structure("Foo", utils::Vector{
+                                     Member("rt", ty.array<f32>()),
+                                 });
+    Structure("Outer", utils::Vector{
+                           Member(Source{{12, 34}}, "inner", ty.Of(foo)),
+                       });
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
@@ -522,13 +531,11 @@
     //   vf: f32;
     // };
 
-    Structure("Foo", {
+    Structure("Foo", utils::Vector{
                          Member(Source{{12, 34}}, "rt", ty.array<f32>()),
                          Member("vf", ty.f32()),
                      });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime arrays may only appear as the last member of a struct)");
@@ -561,16 +568,16 @@
 
     auto* param = Param(Source{{12, 34}}, "a", ty.array<i32>());
 
-    Func("func", {param}, ty.void_(),
-         {
+    Func("func", utils::Vector{param}, ty.void_(),
+         utils::Vector{
              Return(),
          });
 
-    Func("main", {}, ty.void_(),
-         {
+    Func("main", utils::Empty, ty.void_(),
+         utils::Vector{
              Return(),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          });
 
@@ -586,8 +593,8 @@
     auto* param =
         Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::StorageClass::kWorkgroup));
 
-    Func("func", {param}, ty.void_(),
-         {
+    Func("func", utils::Vector{param}, ty.void_(),
+         utils::Vector{
              Return(),
          });
 
@@ -605,13 +612,11 @@
     //}
 
     auto* alias = Alias("RTArr", ty.array<u32>());
-    Structure("s", {
+    Structure("s", utils::Vector{
                        Member(Source{{12, 34}}, "b", ty.Of(alias)),
                        Member("a", ty.u32()),
                    });
 
-    WrapInFunction();
-
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
               "12:34 error: runtime arrays may only appear as the last member of a struct");
@@ -625,13 +630,11 @@
     //}
 
     auto* alias = Alias("RTArr", ty.array<u32>());
-    Structure("s", {
+    Structure("s", utils::Vector{
                        Member("a", ty.u32()),
                        Member("b", ty.Of(alias)),
                    });
 
-    WrapInFunction();
-
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -659,7 +662,7 @@
 TEST_F(ResolverTypeValidationTest, FunctionAsType) {
     // fn f() {}
     // var<private> v : f;
-    Func("f", {}, ty.void_(), {});
+    Func("f", utils::Empty, ty.void_(), {});
     GlobalVar("v", ty.type_name("f"), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
@@ -761,7 +764,10 @@
 TEST_P(SampledTextureDimensionTest, All) {
     auto& params = GetParam();
     GlobalVar(Source{{12, 34}}, "a", ty.sampled_texture(params.dim, ty.i32()),
-              ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+              ast::StorageClass::kNone, nullptr,
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -779,7 +785,10 @@
 TEST_P(MultisampledTextureDimensionTest, All) {
     auto& params = GetParam();
     GlobalVar("a", ty.multisampled_texture(Source{{12, 34}}, params.dim, ty.i32()),
-              ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+              ast::StorageClass::kNone, nullptr,
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -832,7 +841,10 @@
     GlobalVar(
         "a",
         ty.sampled_texture(Source{{12, 34}}, ast::TextureDimension::k2d, params.type_func(*this)),
-        ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+        ast::StorageClass::kNone, nullptr,
+        utils::Vector{
+            GroupAndBinding(0, 0),
+        });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -851,7 +863,10 @@
     GlobalVar("a",
               ty.multisampled_texture(Source{{12, 34}}, ast::TextureDimension::k2d,
                                       params.type_func(*this)),
-              ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+              ast::StorageClass::kNone, nullptr,
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -890,7 +905,10 @@
     auto* st = ty.storage_texture(Source{{12, 34}}, params.dim, ast::TexelFormat::kR32Uint,
                                   ast::Access::kWrite);
 
-    GlobalVar("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone,
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -940,17 +958,29 @@
 
     auto* st_a = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d, params.format,
                                     ast::Access::kWrite);
-    GlobalVar("a", st_a, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st_a, ast::StorageClass::kNone,
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     auto* st_b = ty.storage_texture(ast::TextureDimension::k2d, params.format, ast::Access::kWrite);
-    GlobalVar("b", st_b, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 1)});
+    GlobalVar("b", st_b, ast::StorageClass::kNone,
+              utils::Vector{
+                  GroupAndBinding(0, 1),
+              });
 
     auto* st_c =
         ty.storage_texture(ast::TextureDimension::k2dArray, params.format, ast::Access::kWrite);
-    GlobalVar("c", st_c, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 2)});
+    GlobalVar("c", st_c, ast::StorageClass::kNone,
+              utils::Vector{
+                  GroupAndBinding(0, 2),
+              });
 
     auto* st_d = ty.storage_texture(ast::TextureDimension::k3d, params.format, ast::Access::kWrite);
-    GlobalVar("d", st_d, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 3)});
+    GlobalVar("d", st_d, ast::StorageClass::kNone,
+              utils::Vector{
+                  GroupAndBinding(0, 3),
+              });
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -974,7 +1004,10 @@
     auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
                                   ast::TexelFormat::kR32Uint, ast::Access::kUndefined);
 
-    GlobalVar("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone,
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: storage texture missing access control");
@@ -988,7 +1021,9 @@
                                   ast::TexelFormat::kR32Uint, ast::Access::kReadWrite);
 
     GlobalVar("a", st, ast::StorageClass::kNone, nullptr,
-              ast::AttributeList{GroupAndBinding(0, 0)});
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1003,7 +1038,9 @@
                                   ast::TexelFormat::kR32Uint, ast::Access::kRead);
 
     GlobalVar("a", st, ast::StorageClass::kNone, nullptr,
-              ast::AttributeList{GroupAndBinding(0, 0)});
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1018,7 +1055,9 @@
                                   ast::Access::kWrite);
 
     GlobalVar("a", st, ast::StorageClass::kNone, nullptr,
-              ast::AttributeList{GroupAndBinding(0, 0)});
+              utils::Vector{
+                  GroupAndBinding(0, 0),
+              });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 905fc0e..555ea1a 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -152,8 +152,8 @@
         }
 
         // Create nodes for parameters.
-        parameters.resize(func->params.size());
-        for (size_t i = 0; i < func->params.size(); i++) {
+        parameters.resize(func->params.Length());
+        for (size_t i = 0; i < func->params.Length(); i++) {
             auto* param = func->params[i];
             auto param_name = builder->Symbols().NameFor(param->symbol);
             auto* sem = builder->Sem().Get<sem::Parameter>(param);
@@ -349,7 +349,7 @@
 
             // Set the parameter tag to ParameterRequiredToBeUniform for each parameter node that
             // was reachable.
-            for (size_t i = 0; i < func->params.size(); i++) {
+            for (size_t i = 0; i < func->params.Length(); i++) {
                 auto* param = func->params[i];
                 if (reachable.contains(current_function_->variables.Get(sem_.Get(param)))) {
                     current_function_->parameters[i].tag = ParameterRequiredToBeUniform;
@@ -367,7 +367,7 @@
 
             // Set the parameter tag to ParameterRequiredToBeUniformForSubsequentControlFlow for
             // each parameter node that was reachable.
-            for (size_t i = 0; i < func->params.size(); i++) {
+            for (size_t i = 0; i < func->params.Length(); i++) {
                 auto* param = func->params[i];
                 if (reachable.contains(current_function_->variables.Get(sem_.Get(param)))) {
                     current_function_->parameters[i].tag =
@@ -386,7 +386,7 @@
 
             // Set the parameter tag to ParameterRequiredToBeUniformForReturnValue for each
             // parameter node that was reachable.
-            for (size_t i = 0; i < func->params.size(); i++) {
+            for (size_t i = 0; i < func->params.Length(); i++) {
                 auto* param = func->params[i];
                 if (reachable.contains(current_function_->variables.Get(sem_.Get(param)))) {
                     current_function_->parameters[i].tag =
@@ -396,7 +396,7 @@
         }
 
         // Traverse the graph for each pointer parameter.
-        for (size_t i = 0; i < func->params.size(); i++) {
+        for (size_t i = 0; i < func->params.Length(); i++) {
             if (current_function_->parameters[i].pointer_return_value == nullptr) {
                 continue;
             }
@@ -411,7 +411,7 @@
             }
 
             // Check every other parameter to see if they feed into this parameter's final value.
-            for (size_t j = 0; j < func->params.size(); j++) {
+            for (size_t j = 0; j < func->params.Length(); j++) {
                 auto* param_source = sem_.Get<sem::Parameter>(func->params[j]);
                 if (reachable.contains(current_function_->parameters[j].init_value)) {
                     current_function_->parameters[i].pointer_param_output_sources.push_back(
@@ -1204,7 +1204,7 @@
         // Process call arguments
         Node* cf_last_arg = cf;
         std::vector<Node*> args;
-        for (size_t i = 0; i < call->args.size(); i++) {
+        for (size_t i = 0; i < call->args.Length(); i++) {
             auto [cf_i, arg_i] = ProcessExpression(cf_last_arg, call->args[i]);
 
             // Capture the index of this argument in a new node.
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index b335c27..2f5fc8f 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -5299,18 +5299,18 @@
     //   ...
     //   *p254 = rhs;
     // }
-    ast::ParameterList params;
-    ast::StatementList foo_body;
+    utils::Vector<const ast::Parameter*, 8> params;
+    utils::Vector<const ast::Statement*, 8> foo_body;
     const ast::Expression* rhs_init = b.Deref("p0");
     for (int i = 1; i < 255; i++) {
         rhs_init = b.Add(rhs_init, b.Deref("p" + std::to_string(i)));
     }
-    foo_body.push_back(b.Decl(b.Let("rhs", nullptr, rhs_init)));
+    foo_body.Push(b.Decl(b.Let("rhs", nullptr, rhs_init)));
     for (int i = 0; i < 255; i++) {
-        params.push_back(
+        params.Push(
             b.Param("p" + std::to_string(i), ty.pointer(ty.i32(), ast::StorageClass::kFunction)));
         if (i > 0) {
-            foo_body.push_back(b.Assign(b.Deref("p" + std::to_string(i)), "rhs"));
+            foo_body.Push(b.Assign(b.Deref("p" + std::to_string(i)), "rhs"));
         }
     }
     b.Func("foo", std::move(params), ty.void_(), foo_body);
@@ -5328,18 +5328,17 @@
     //   }
     // }
     b.GlobalVar("non_uniform_global", ty.i32(), ast::StorageClass::kPrivate);
-    ast::StatementList main_body;
-    ast::ExpressionList args;
+    utils::Vector<const ast::Statement*, 8> main_body;
+    utils::Vector<const ast::Expression*, 8> args;
     for (int i = 0; i < 255; i++) {
         auto name = "v" + std::to_string(i);
-        main_body.push_back(b.Decl(b.Var(name, ty.i32())));
-        args.push_back(b.AddressOf(name));
+        main_body.Push(b.Decl(b.Var(name, ty.i32())));
+        args.Push(b.AddressOf(name));
     }
-    main_body.push_back(b.Assign("v0", "non_uniform_global"));
-    main_body.push_back(b.CallStmt(b.create<ast::CallExpression>(b.Expr("foo"), args)));
-    main_body.push_back(
-        b.If(b.Equal("v254", 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
-    b.Func("main", {}, ty.void_(), main_body);
+    main_body.Push(b.Assign("v0", "non_uniform_global"));
+    main_body.Push(b.CallStmt(b.create<ast::CallExpression>(b.Expr("foo"), args)));
+    main_body.Push(b.If(b.Equal("v254", 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
+    b.Func("main", utils::Empty, ty.void_(), main_body);
 
     // TODO(jrprice): Expect false when uniformity issues become errors.
     EXPECT_TRUE(RunTest(std::move(b))) << error_;
@@ -6539,15 +6538,15 @@
     //   }
     // }
     b.GlobalVar("v0", ty.i32(), ast::StorageClass::kPrivate, b.Expr(0_i));
-    ast::StatementList foo_body;
+    utils::Vector<const ast::Statement*, 8> foo_body;
     std::string v_last = "v0";
     for (int i = 1; i < 100000; i++) {
         auto v = "v" + std::to_string(i);
-        foo_body.push_back(b.Decl(b.Var(v, nullptr, b.Expr(v_last))));
+        foo_body.Push(b.Decl(b.Var(v, nullptr, b.Expr(v_last))));
         v_last = v;
     }
-    foo_body.push_back(b.If(b.Equal(v_last, 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
-    b.Func("foo", {}, ty.void_(), foo_body);
+    foo_body.Push(b.If(b.Equal(v_last, 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
+    b.Func("foo", utils::Empty, ty.void_(), foo_body);
 
     // TODO(jrprice): Expect false when uniformity issues become errors.
     EXPECT_TRUE(RunTest(std::move(b))) << error_;
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 45c6d87..52d4c87 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -65,15 +65,15 @@
     GlobalVar("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
-    Func(Source{{9, 10}}, "f0", {}, ty.vec4<f32>(),
-         {
+    Func(Source{{9, 10}}, "f0", utils::Empty, ty.vec4<f32>(),
+         utils::Vector{
              stmt,
              Return(Expr("dst")),
          },
-         {
+         utils::Vector{
              Stage(ast::PipelineStage::kVertex),
          },
-         {
+         utils::Vector{
              Builtin(ast::BuiltinValue::kPosition),
          });
 
@@ -97,10 +97,15 @@
     GlobalVar("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
-    Func(Source{{5, 6}}, "f2", {}, ty.void_(), {stmt});
-    Func(Source{{7, 8}}, "f1", {}, ty.void_(), {CallStmt(Call("f2"))});
-    Func(Source{{9, 10}}, "f0", {}, ty.void_(), {CallStmt(Call("f1"))},
-         ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+    Func(Source{{5, 6}}, "f2", utils::Empty, ty.void_(), utils::Vector{stmt});
+    Func(Source{{7, 8}}, "f1", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("f2")),
+         });
+    Func(Source{{9, 10}}, "f0", utils::Empty, ty.void_(), utils::Vector{CallStmt(Call("f1"))},
+         utils::Vector{
+             Stage(ast::PipelineStage::kFragment),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -153,7 +158,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_DontCall_Function) {
-    Func("func", {}, ty.void_(), {}, {});
+    Func("func", utils::Empty, ty.void_(), utils::Empty, {});
     WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "func"));
 
     EXPECT_FALSE(r()->Resolve());
@@ -223,8 +228,8 @@
 
     GlobalVar("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
 
-    Func("my_func", {}, ty.void_(),
-         {
+    Func("my_func", utils::Empty, ty.void_(),
+         utils::Vector{
              Assign(Expr(Source{{12, 34}}, "global_var"), 3.14_f),
              Return(),
          });
@@ -297,8 +302,8 @@
 TEST_F(ResolverValidationTest, StorageClass_FunctionVariableWorkgroupClass) {
     auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
 
-    Func("func", {}, ty.void_(),
-         {
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
          });
 
@@ -311,8 +316,8 @@
 TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
     auto* var = Var("s", ty.i32(), ast::StorageClass::kPrivate);
 
-    Func("func", {}, ty.void_(),
-         {
+    Func("func", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(var),
          });
 
@@ -325,7 +330,7 @@
 TEST_F(ResolverValidationTest, StorageClass_SamplerExplicitStorageClass) {
     auto* t = ty.sampler(ast::SamplerKind::kSampler);
     GlobalVar(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -339,7 +344,7 @@
 TEST_F(ResolverValidationTest, StorageClass_TextureExplicitStorageClass) {
     auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
     GlobalVar(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
-              ast::AttributeList{
+              utils::Vector{
                   create<ast::BindingAttribute>(0u),
                   create<ast::GroupAttribute>(0u),
               });
@@ -425,7 +430,11 @@
     auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
     auto* accessor_expr = MemberAccessor(star_p, z);
     auto* x = Var("x", ty.f32(), accessor_expr);
-    Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
+    Func("func", utils::Vector{p}, ty.f32(),
+         utils::Vector{
+             Decl(x),
+             Return(x),
+         });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -439,7 +448,11 @@
     auto* accessor_expr = MemberAccessor(p, z);
     auto* star_p = Deref(accessor_expr);
     auto* x = Var("x", ty.f32(), star_p);
-    Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
+    Func("func", utils::Vector{p}, ty.f32(),
+         utils::Vector{
+             Decl(x),
+             Return(x),
+         });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -806,8 +819,14 @@
     //   }
     // }
 
-    Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
-    Func("SomeFunc", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
+    Func("MayDiscard", utils::Empty, ty.void_(),
+         utils::Vector{
+             If(true, Block(Discard())),
+         });
+    Func("SomeFunc", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("MayDiscard")),
+         });
 
     WrapInFunction(Loop(         // outer loop
         Block(),                 //   outer loop block
@@ -926,8 +945,14 @@
     //   break;
     // }
 
-    Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
-    Func("F", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
+    Func("MayDiscard", utils::Empty, ty.void_(),
+         utils::Vector{
+             If(true, Block(Discard())),
+         });
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{
+             CallStmt(Call("MayDiscard")),
+         });
 
     WrapInFunction(For(nullptr, nullptr,
                        Loop(Source{{56, 78}},                               //
@@ -1191,30 +1216,40 @@
 }
 
 TEST_F(ResolverValidationTest, StructMemberDuplicateName) {
-    Structure("S",
-              {Member(Source{{12, 34}}, "a", ty.i32()), Member(Source{{56, 78}}, "a", ty.i32())});
+    Structure("S", utils::Vector{
+                       Member(Source{{12, 34}}, "a", ty.i32()),
+                       Member(Source{{56, 78}}, "a", ty.i32()),
+                   });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "56:78 error: redefinition of 'a'\n12:34 note: previous definition "
               "is here");
 }
 TEST_F(ResolverValidationTest, StructMemberDuplicateNameDifferentTypes) {
-    Structure("S", {Member(Source{{12, 34}}, "a", ty.bool_()),
-                    Member(Source{{12, 34}}, "a", ty.vec3<f32>())});
+    Structure("S", utils::Vector{
+                       Member(Source{{12, 34}}, "a", ty.bool_()),
+                       Member(Source{{12, 34}}, "a", ty.vec3<f32>()),
+                   });
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: redefinition of 'a'\n12:34 note: previous definition "
               "is here");
 }
 TEST_F(ResolverValidationTest, StructMemberDuplicateNamePass) {
-    Structure("S", {Member("a", ty.i32()), Member("b", ty.f32())});
-    Structure("S1", {Member("a", ty.i32()), Member("b", ty.f32())});
+    Structure("S", utils::Vector{
+                       Member("a", ty.i32()),
+                       Member("b", ty.f32()),
+                   });
+    Structure("S1", utils::Vector{
+                        Member("a", ty.i32()),
+                        Member("b", ty.f32()),
+                    });
     EXPECT_TRUE(r()->Resolve());
 }
 
 TEST_F(ResolverValidationTest, NonPOTStructMemberAlignAttribute) {
-    Structure("S", {
-                       Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 3)}),
+    Structure("S", utils::Vector{
+                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, 3)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1222,8 +1257,8 @@
 }
 
 TEST_F(ResolverValidationTest, ZeroStructMemberAlignAttribute) {
-    Structure("S", {
-                       Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 0)}),
+    Structure("S", utils::Vector{
+                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, 0)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1231,8 +1266,8 @@
 }
 
 TEST_F(ResolverValidationTest, ZeroStructMemberSizeAttribute) {
-    Structure("S", {
-                       Member("a", ty.f32(), {MemberSize(Source{{12, 34}}, 0)}),
+    Structure("S", utils::Vector{
+                       Member("a", ty.f32(), utils::Vector{MemberSize(Source{{12, 34}}, 0)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1240,8 +1275,9 @@
 }
 
 TEST_F(ResolverValidationTest, OffsetAndSizeAttribute) {
-    Structure("S", {
-                       Member(Source{{12, 34}}, "a", ty.f32(), {MemberOffset(0), MemberSize(4)}),
+    Structure("S", utils::Vector{
+                       Member(Source{{12, 34}}, "a", ty.f32(),
+                              utils::Vector{MemberOffset(0), MemberSize(4)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1251,8 +1287,9 @@
 }
 
 TEST_F(ResolverValidationTest, OffsetAndAlignAttribute) {
-    Structure("S", {
-                       Member(Source{{12, 34}}, "a", ty.f32(), {MemberOffset(0), MemberAlign(4)}),
+    Structure("S", utils::Vector{
+                       Member(Source{{12, 34}}, "a", ty.f32(),
+                              utils::Vector{MemberOffset(0), MemberAlign(4)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1262,9 +1299,9 @@
 }
 
 TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeAttribute) {
-    Structure("S", {
+    Structure("S", utils::Vector{
                        Member(Source{{12, 34}}, "a", ty.f32(),
-                              {MemberOffset(0), MemberAlign(4), MemberSize(4)}),
+                              utils::Vector{MemberOffset(0), MemberAlign(4), MemberSize(4)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 61f13cb..f3a02ed 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -641,7 +641,7 @@
         },
         [&](const ast::Override*) { return Override(global, override_ids); },
         [&](const ast::Const*) {
-            if (!decl->attributes.empty()) {
+            if (!decl->attributes.IsEmpty()) {
                 AddError("attribute is not valid for module-scope 'const' declaration",
                          decl->attributes[0]->source);
                 return false;
@@ -1044,7 +1044,7 @@
         }
     }
 
-    if (decl->params.size() > 255) {
+    if (decl->params.Length() > 255) {
         AddError("functions may declare at most 255 parameters", decl->source);
         return false;
     }
@@ -1126,12 +1126,13 @@
     };
 
     // Inner lambda that is applied to a type and all of its members.
-    auto validate_entry_point_attributes_inner = [&](const ast::AttributeList& attrs,
+    auto validate_entry_point_attributes_inner = [&](utils::VectorRef<const ast::Attribute*> attrs,
                                                      const sem::Type* ty, Source source,
                                                      ParamOrRetType param_or_ret,
                                                      bool is_struct_member) {
         // Temporally forbid using f16 types in entry point IO.
-        // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in entry point IO.
+        // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in entry point
+        // IO.
         if (Is<sem::F16>(sem::Type::DeepestElementOf(ty))) {
             AddError("entry point IO of f16 types is not implemented yet", source);
             return false;
@@ -1269,8 +1270,9 @@
     };
 
     // Outer lambda for validating the entry point attributes for a type.
-    auto validate_entry_point_attributes = [&](const ast::AttributeList& attrs, const sem::Type* ty,
-                                               Source source, ParamOrRetType param_or_ret) {
+    auto validate_entry_point_attributes = [&](utils::VectorRef<const ast::Attribute*> attrs,
+                                               const sem::Type* ty, Source source,
+                                               ParamOrRetType param_or_ret) {
         if (!validate_entry_point_attributes_inner(attrs, ty, source, param_or_ret,
                                                    /*is_struct_member*/ false)) {
             return false;
@@ -1379,7 +1381,7 @@
     return true;
 }
 
-bool Validator::Statements(const ast::StatementList& stmts) const {
+bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
     for (auto* stmt : stmts) {
         if (!sem_.Get(stmt)->IsReachable()) {
             /// TODO(https://github.com/gpuweb/gpuweb/issues/2378): This may need to
@@ -1443,7 +1445,7 @@
                 return fail("break statement is not directly in if statement block",
                             stmt->Declaration()->source);
             }
-            if (block->Declaration()->statements.size() != 1) {
+            if (block->Declaration()->statements.Length() != 1) {
                 return fail("if statement block contains multiple statements",
                             block->Declaration()->source);
             }
@@ -1542,7 +1544,7 @@
         if (auto* c = As<sem::CaseStatement>(block->Parent())) {
             if (block->Declaration()->Last() == stmt->Declaration()) {
                 if (auto* s = As<sem::SwitchStatement>(c->Parent())) {
-                    if (c->Declaration() != s->Declaration()->body.back()) {
+                    if (c->Declaration() != s->Declaration()->body.Back()) {
                         return true;
                     }
                     AddError(
@@ -1749,8 +1751,8 @@
         return false;
     }
 
-    if (decl->args.size() != target->Parameters().Length()) {
-        bool more = decl->args.size() > target->Parameters().Length();
+    if (decl->args.Length() != target->Parameters().Length()) {
+        bool more = decl->args.Length() > target->Parameters().Length();
         AddError("too " + (more ? std::string("many") : std::string("few")) +
                      " arguments in call to '" + name + "', expected " +
                      std::to_string(target->Parameters().Length()) + ", got " +
@@ -1847,12 +1849,12 @@
         return false;
     }
 
-    if (ctor->args.size() > 0) {
-        if (ctor->args.size() != struct_type->Members().size()) {
-            std::string fm = ctor->args.size() < struct_type->Members().size() ? "few" : "many";
+    if (ctor->args.Length() > 0) {
+        if (ctor->args.Length() != struct_type->Members().size()) {
+            std::string fm = ctor->args.Length() < struct_type->Members().size() ? "few" : "many";
             AddError("struct constructor has too " + fm + " inputs: expected " +
                          std::to_string(struct_type->Members().size()) + ", found " +
-                         std::to_string(ctor->args.size()),
+                         std::to_string(ctor->args.Length()),
                      ctor->source);
             return false;
         }
@@ -1894,17 +1896,17 @@
     } else if (!elem_ty->IsConstructible()) {
         AddError("array constructor has non-constructible element type", ctor->source);
         return false;
-    } else if (!values.empty() && (values.size() != array_type->Count())) {
-        std::string fm = values.size() < array_type->Count() ? "few" : "many";
+    } else if (!values.IsEmpty() && (values.Length() != array_type->Count())) {
+        std::string fm = values.Length() < array_type->Count() ? "few" : "many";
         AddError("array constructor has too " + fm + " elements: expected " +
                      std::to_string(array_type->Count()) + ", found " +
-                     std::to_string(values.size()),
+                     std::to_string(values.Length()),
                  ctor->source);
         return false;
-    } else if (values.size() > array_type->Count()) {
+    } else if (values.Length() > array_type->Count()) {
         AddError("array constructor has too many elements: expected " +
                      std::to_string(array_type->Count()) + ", found " +
-                     std::to_string(values.size()),
+                     std::to_string(values.Length()),
                  ctor->source);
         return false;
     }
@@ -2448,7 +2450,7 @@
     return true;
 }
 
-bool Validator::NoDuplicateAttributes(const ast::AttributeList& attributes) const {
+bool Validator::NoDuplicateAttributes(utils::VectorRef<const ast::Attribute*> attributes) const {
     std::unordered_map<const TypeInfo*, Source> seen;
     for (auto* d : attributes) {
         auto res = seen.emplace(&d->TypeInfo(), d->source);
@@ -2461,7 +2463,7 @@
     return true;
 }
 
-bool Validator::IsValidationDisabled(const ast::AttributeList& attributes,
+bool Validator::IsValidationDisabled(utils::VectorRef<const ast::Attribute*> attributes,
                                      ast::DisabledValidation validation) const {
     for (auto* attribute : attributes) {
         if (auto* dv = attribute->As<ast::DisableValidationAttribute>()) {
@@ -2473,7 +2475,7 @@
     return false;
 }
 
-bool Validator::IsValidationEnabled(const ast::AttributeList& attributes,
+bool Validator::IsValidationEnabled(utils::VectorRef<const ast::Attribute*> attributes,
                                     ast::DisabledValidation validation) const {
     return !IsValidationDisabled(attributes, validation);
 }
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 935195e..57ac064 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -325,7 +325,7 @@
     /// Validates a list of statements
     /// @param stmts the statements to validate
     /// @returns true on success, false otherwise
-    bool Statements(const ast::StatementList& stmts) const;
+    bool Statements(utils::VectorRef<const ast::Statement*> stmts) const;
 
     /// Validates a storage texture
     /// @param t the texture to validate
@@ -422,7 +422,7 @@
     /// Validates there are no duplicate attributes
     /// @param attributes the list of attributes to validate
     /// @returns true on success, false otherwise.
-    bool NoDuplicateAttributes(const ast::AttributeList& attributes) const;
+    bool NoDuplicateAttributes(utils::VectorRef<const ast::Attribute*> attributes) const;
 
     /// Validates a storage class layout
     /// @param type the type to validate
@@ -449,7 +449,7 @@
     /// `validation`
     /// @param attributes the attribute list to check
     /// @param validation the validation mode to check
-    bool IsValidationDisabled(const ast::AttributeList& attributes,
+    bool IsValidationDisabled(utils::VectorRef<const ast::Attribute*> attributes,
                               ast::DisabledValidation validation) const;
 
     /// @returns true if the attribute list does not contains a
@@ -457,7 +457,7 @@
     /// `validation`
     /// @param attributes the attribute list to check
     /// @param validation the validation mode to check
-    bool IsValidationEnabled(const ast::AttributeList& attributes,
+    bool IsValidationEnabled(utils::VectorRef<const ast::Attribute*> attributes,
                              ast::DisabledValidation validation) const;
 
   private:
diff --git a/src/tint/resolver/variable_test.cc b/src/tint/resolver/variable_test.cc
index 49c1f8b..cc4bd65 100644
--- a/src/tint/resolver/variable_test.cc
+++ b/src/tint/resolver/variable_test.cc
@@ -43,7 +43,7 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* S = Structure("S", {Member("i", ty.i32())});
+    auto* S = Structure("S", utils::Vector{Member("i", ty.i32())});
     auto* A = Alias("A", ty.Of(S));
 
     auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
@@ -54,8 +54,8 @@
     auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone);
     auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone);
 
-    Func("F", {}, ty.void_(),
-         {
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(i),
              Decl(u),
              Decl(f),
@@ -108,7 +108,7 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* S = Structure("S", {Member("i", ty.i32())});
+    auto* S = Structure("S", utils::Vector{Member("i", ty.i32())});
     auto* A = Alias("A", ty.Of(S));
 
     auto* i_c = Expr(1_i);
@@ -127,8 +127,8 @@
     auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone, s_c);
     auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone, a_c);
 
-    Func("F", {}, ty.void_(),
-         {
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(i),
              Decl(u),
              Decl(f),
@@ -182,7 +182,7 @@
 
     auto* t = Alias("a", ty.i32());
     auto* v = Var("a", nullptr, Expr(false));
-    Func("F", {}, ty.void_(), {Decl(v)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -201,9 +201,9 @@
     //   var a = true;
     // }
 
-    auto* t = Structure("a", {Member("m", ty.i32())});
+    auto* t = Structure("a", utils::Vector{Member("m", ty.i32())});
     auto* v = Var("a", nullptr, Expr(false));
-    Func("F", {}, ty.void_(), {Decl(v)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -219,7 +219,7 @@
     // }
 
     auto* v = Var("a", nullptr, Expr(false));
-    auto* f = Func("a", {}, ty.void_(), {Decl(v)});
+    auto* f = Func("a", utils::Empty, ty.void_(), utils::Vector{Decl(v)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -240,7 +240,7 @@
 
     auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
     auto* v = Var("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(v)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -263,7 +263,7 @@
 
     auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
     auto* v = Var("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(v)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -287,7 +287,7 @@
 
     auto* x = Var("a", ty.i32(), Expr(1_i));
     auto* y = Var("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(x), Block(Decl(y))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -313,7 +313,7 @@
 
     auto* c = Const("a", ty.i32(), Expr(1_i));
     auto* v = Var("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(c), Block(Decl(v))});
+    Func("X", utils::Empty, ty.void_(), utils::Vector{Decl(c), Block(Decl(v))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -339,7 +339,7 @@
 
     auto* l = Let("a", ty.i32(), Expr(1_i));
     auto* v = Var("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(l), Block(Decl(v))});
+    Func("X", utils::Empty, ty.void_(), utils::Vector{Decl(l), Block(Decl(v))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -364,7 +364,7 @@
 
     auto* p = Param("a", ty.i32());
     auto* v = Var("a", nullptr, Expr("a"));
-    Func("X", {p}, ty.void_(), {Block(Decl(v))});
+    Func("X", utils::Vector{p}, ty.void_(), utils::Vector{Block(Decl(v))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -399,7 +399,7 @@
 
     Enable(ast::Extension::kF16);
 
-    auto* S = Structure("S", {Member("i", ty.i32())});
+    auto* S = Structure("S", utils::Vector{Member("i", ty.i32())});
     auto* A = Alias("A", ty.Of(S));
     auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
 
@@ -421,8 +421,8 @@
     auto* a = Let("a", ty.Of(A), a_c);
     auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
 
-    Func("F", {}, ty.void_(),
-         {
+    Func("F", utils::Empty, ty.void_(),
+         utils::Vector{
              Decl(v),
              Decl(i),
              Decl(u),
@@ -468,10 +468,10 @@
     // fn f() {
     //   let p = &s.inner.arr[4];
     // }
-    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+    auto* inner = Structure("Inner", utils::Vector{Member("arr", ty.array<i32, 4>())});
+    auto* buf = Structure("S", utils::Vector{Member("inner", ty.Of(inner))});
     auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(0u),
                                   create<ast::GroupAttribute>(0u),
                               });
@@ -499,7 +499,7 @@
 
     auto* t = Alias("a", ty.i32());
     auto* l = Let("a", nullptr, Expr(false));
-    Func("F", {}, ty.void_(), {Decl(l)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(l)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -518,9 +518,9 @@
     //   let a = false;
     // }
 
-    auto* t = Structure("a", {Member("m", ty.i32())});
+    auto* t = Structure("a", utils::Vector{Member("m", ty.i32())});
     auto* l = Let("a", nullptr, Expr(false));
-    Func("F", {}, ty.void_(), {Decl(l)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(l)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -536,7 +536,7 @@
     // }
 
     auto* l = Let("a", nullptr, Expr(false));
-    auto* fb = Func("a", {}, ty.void_(), {Decl(l)});
+    auto* fb = Func("a", utils::Empty, ty.void_(), utils::Vector{Decl(l)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -557,7 +557,7 @@
 
     auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
     auto* l = Let("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(l)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(l)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -580,7 +580,7 @@
 
     auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
     auto* l = Let("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(l)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(l)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -604,7 +604,7 @@
 
     auto* v = Var("a", ty.i32(), Expr(1_i));
     auto* l = Let("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(v), Block(Decl(l))});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v), Block(Decl(l))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -630,7 +630,7 @@
 
     auto* x = Const("a", ty.i32(), Expr(1_i));
     auto* y = Let("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+    Func("X", utils::Empty, ty.void_(), utils::Vector{Decl(x), Block(Decl(y))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -656,7 +656,7 @@
 
     auto* x = Let("a", ty.i32(), Expr(1_i));
     auto* y = Let("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+    Func("X", utils::Empty, ty.void_(), utils::Vector{Decl(x), Block(Decl(y))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -681,7 +681,7 @@
 
     auto* p = Param("a", ty.i32());
     auto* l = Let("a", nullptr, Expr("a"));
-    Func("X", {p}, ty.void_(), {Block(Decl(l))});
+    Func("X", utils::Vector{p}, ty.void_(), utils::Vector{Block(Decl(l))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -709,7 +709,7 @@
 
     auto* t = Alias("a", ty.i32());
     auto* c = Const("a", nullptr, Expr(false));
-    Func("F", {}, ty.void_(), {Decl(c)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(c)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -728,9 +728,9 @@
     //   const a = false;
     // }
 
-    auto* t = Structure("a", {Member("m", ty.i32())});
+    auto* t = Structure("a", utils::Vector{Member("m", ty.i32())});
     auto* c = Const("a", nullptr, Expr(false));
-    Func("F", {}, ty.void_(), {Decl(c)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(c)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -746,7 +746,7 @@
     // }
 
     auto* c = Const("a", nullptr, Expr(false));
-    auto* fb = Func("a", {}, ty.void_(), {Decl(c)});
+    auto* fb = Func("a", utils::Empty, ty.void_(), utils::Vector{Decl(c)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -767,7 +767,7 @@
 
     auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
     auto* c = Const("a", nullptr, Expr(1_i));
-    Func("F", {}, ty.void_(), {Decl(c)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(c)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -786,7 +786,7 @@
 
     auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
     auto* c = Const("a", nullptr, Expr("a"));
-    Func("F", {}, ty.void_(), {Decl(c)});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(c)});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -810,7 +810,7 @@
 
     auto* v = Var("a", ty.i32(), Expr(1_i));
     auto* c = Const("a", nullptr, Expr(1_i));
-    Func("F", {}, ty.void_(), {Decl(v), Block(Decl(c))});
+    Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v), Block(Decl(c))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -832,7 +832,7 @@
 
     auto* x = Const("a", ty.i32(), Expr(1_i));
     auto* y = Const("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+    Func("X", utils::Empty, ty.void_(), utils::Vector{Decl(x), Block(Decl(y))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -858,7 +858,7 @@
 
     auto* l = Let("a", ty.i32(), Expr(1_i));
     auto* c = Const("a", nullptr, Expr(1_i));
-    Func("X", {}, ty.void_(), {Decl(l), Block(Decl(c))});
+    Func("X", utils::Empty, ty.void_(), utils::Vector{Decl(l), Block(Decl(c))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -879,7 +879,7 @@
 
     auto* p = Param("a", ty.i32());
     auto* c = Const("a", nullptr, Expr(1_i));
-    Func("X", {p}, ty.void_(), {Block(Decl(c))});
+    Func("X", utils::Vector{p}, ty.void_(), utils::Vector{Block(Decl(c))});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -892,7 +892,7 @@
 }
 
 TEST_F(ResolverVariableTest, LocalConst_ExplicitType_Decls) {
-    Structure("S", {Member("m", ty.u32())});
+    Structure("S", utils::Vector{Member("m", ty.u32())});
 
     auto* c_i32 = Const("a", ty.i32(), Expr(0_i));
     auto* c_u32 = Const("b", ty.u32(), Expr(0_u));
@@ -936,7 +936,7 @@
 }
 
 TEST_F(ResolverVariableTest, LocalConst_ImplicitType_Decls) {
-    Structure("S", {Member("m", ty.u32())});
+    Structure("S", utils::Vector{Member("m", ty.u32())});
 
     auto* c_i32 = Const("a", nullptr, Expr(0_i));
     auto* c_u32 = Const("b", nullptr, Expr(0_u));
@@ -1037,21 +1037,21 @@
 TEST_F(ResolverVariableTest, GlobalVar_StorageClass) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
-    auto* buf = Structure("S", {Member("m", ty.i32())});
+    auto* buf = Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* private_ = GlobalVar("p", ty.i32(), ast::StorageClass::kPrivate);
     auto* workgroup = GlobalVar("w", ty.i32(), ast::StorageClass::kWorkgroup);
     auto* uniform = GlobalVar("ub", ty.Of(buf), ast::StorageClass::kUniform,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(0u),
                                   create<ast::GroupAttribute>(0u),
                               });
     auto* storage = GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(1u),
                                   create<ast::GroupAttribute>(0u),
                               });
     auto* handle = GlobalVar("h", ty.depth_texture(ast::TextureDimension::k2d),
-                             ast::AttributeList{
+                             utils::Vector{
                                  create<ast::BindingAttribute>(2u),
                                  create<ast::GroupAttribute>(0u),
                              });
@@ -1074,10 +1074,10 @@
 TEST_F(ResolverVariableTest, GlobalVar_ExplicitStorageClass) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
-    auto* buf = Structure("S", {Member("m", ty.i32())});
+    auto* buf = Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* storage =
         GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                  ast::AttributeList{
+                  utils::Vector{
                       create<ast::BindingAttribute>(1u),
                       create<ast::GroupAttribute>(0u),
                   });
@@ -1219,7 +1219,7 @@
     // }
 
     auto* p = Param("a", ty.bool_());
-    auto* f = Func("a", {p}, ty.void_(), {});
+    auto* f = Func("a", utils::Vector{p}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1240,7 +1240,7 @@
 
     auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
     auto* p = Param("a", ty.bool_());
-    Func("F", {p}, ty.void_(), {});
+    Func("F", utils::Vector{p}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1261,7 +1261,7 @@
 
     auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
     auto* p = Param("a", ty.bool_());
-    Func("F", {p}, ty.void_(), {});
+    Func("F", utils::Vector{p}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1282,7 +1282,7 @@
 
     auto* a = Alias("a", ty.i32());
     auto* p = Param("a", ty.type_name("a"));
-    Func("F", {p}, ty.void_(), {});
+    Func("F", utils::Vector{p}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 08312fe..97b177e 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -98,7 +98,10 @@
     // ...
     // @id(N) override oN : i32;
     constexpr size_t kLimit = std::numeric_limits<decltype(OverrideId::value)>::max();
-    Override("reserved", ty.i32(), nullptr, {Id(kLimit)});
+    Override("reserved", ty.i32(), nullptr,
+             utils::Vector{
+                 Id(kLimit),
+             });
     for (size_t i = 0; i < kLimit; i++) {
         Override("o" + std::to_string(i), ty.i32(), nullptr);
     }
@@ -288,10 +291,14 @@
     // fn f() {
     //   let p : pointer<storage, i32, read_write> = &s.inner.arr[2i];
     // }
-    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+    auto* inner = Structure("Inner", utils::Vector{
+                                         Member("arr", ty.array<i32, 4>()),
+                                     });
+    auto* buf = Structure("S", utils::Vector{
+                                   Member("inner", ty.Of(inner)),
+                               });
     auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage,
-                              ast::AttributeList{
+                              utils::Vector{
                                   create<ast::BindingAttribute>(0u),
                                   create<ast::GroupAttribute>(0u),
                               });
@@ -319,7 +326,9 @@
 }
 
 TEST_F(ResolverVariableValidationTest, NonConstructibleType_RuntimeArray) {
-    auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))});
+    auto* s = Structure("S", utils::Vector{
+                                 Member(Source{{56, 78}}, "m", ty.array(ty.i32())),
+                             });
     auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
     WrapInFunction(v);
 
@@ -331,7 +340,9 @@
 }
 
 TEST_F(ResolverVariableValidationTest, NonConstructibleType_Struct_WithAtomic) {
-    auto* s = Structure("S", {Member("m", ty.atomic(ty.i32()))});
+    auto* s = Structure("S", utils::Vector{
+                                 Member("m", ty.atomic(ty.i32())),
+                             });
     auto* v = Var("v", ty.Of(s));
     WrapInFunction(v);