Add helper to create StructMember nodes.

This CL updates the ast::Builder to provide help creating struct members
and decorations. The helpers are then used throughout the various files
to simplify the code.

Change-Id: I53af4578190499d9ae2623073f8a44182954e5d9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/35821
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: dan sinclair <dsinclair@chromium.org>
diff --git a/src/ast/builder.h b/src/ast/builder.h
index 6554b73..a450a6e 100644
--- a/src/ast/builder.h
+++ b/src/ast/builder.h
@@ -30,6 +30,9 @@
 #include "src/ast/module.h"
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/sint_literal.h"
+#include "src/ast/struct.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/struct_member_offset_decoration.h"
 #include "src/ast/type/array_type.h"
 #include "src/ast/type/bool_type.h"
 #include "src/ast/type/f32_type.h"
@@ -571,6 +574,34 @@
                                             Expr(std::forward<IDX>(idx)));
   }
 
+  /// Creates a StructMemberOffsetDecoration
+  /// @param val the offset value
+  /// @returns the offset decoration pointer
+  StructMemberOffsetDecoration* MemberOffset(uint32_t val) {
+    return mod->create<StructMemberOffsetDecoration>(source_, val);
+  }
+
+  /// Creates a StructMember
+  /// @param name the struct member name
+  /// @param type the struct member type
+  /// @returns the struct member pointer
+  StructMember* Member(const std::string& name, type::Type* type) {
+    return mod->create<StructMember>(source_, mod->RegisterSymbol(name), name,
+                                     type, StructMemberDecorationList{});
+  }
+
+  /// Creates a StructMember
+  /// @param name the struct member name
+  /// @param type the struct member type
+  /// @param decos the struct member decorations
+  /// @returns the struct member pointer
+  StructMember* Member(const std::string& name,
+                       type::Type* type,
+                       StructMemberDecorationList decos) {
+    return mod->create<StructMember>(source_, mod->RegisterSymbol(name), name,
+                                     type, decos);
+  }
+
   /// Creates a new Node owned by the Module, with the explicit Source.
   /// When the Module is destructed, the `Node` will also be destructed.
   /// @param source the source to apply to the Node
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index 019bb43..ec31937 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -19,6 +19,7 @@
 #include "gtest/gtest.h"
 #include "src/ast/assignment_statement.h"
 #include "src/ast/bool_literal.h"
+#include "src/ast/builder.h"
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
 #include "src/ast/constant_id_decoration.h"
@@ -66,11 +67,11 @@
 namespace inspector {
 namespace {
 
-class InspectorHelper {
+class InspectorHelper : public ast::BuilderWithModule {
  public:
   InspectorHelper()
-      : td_(std::make_unique<TypeDeterminer>(&mod_)),
-        inspector_(std::make_unique<Inspector>(mod_)),
+      : td_(std::make_unique<TypeDeterminer>(mod)),
+        inspector_(std::make_unique<Inspector>(*mod)),
         sampler_type_(ast::type::SamplerKind::kSampler),
         comparison_sampler_type_(ast::type::SamplerKind::kComparisonSampler) {}
 
@@ -85,8 +86,8 @@
         Source{}, ast::StatementList{
                       create<ast::ReturnStatement>(Source{}),
                   });
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(name), name,
-                                 ast::VariableList(), void_type(), body,
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(name), name,
+                                 ast::VariableList(), ty.void_, body,
                                  decorations);
   }
 
@@ -100,7 +101,7 @@
       std::string callee,
       ast::FunctionDecorationList decorations = {}) {
     auto* ident_expr = create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(callee), callee);
+        Source{}, mod->RegisterSymbol(callee), callee);
     auto* call_expr = create<ast::CallExpression>(Source{}, ident_expr,
                                                   ast::ExpressionList());
     auto* body = create<ast::BlockStatement>(
@@ -108,8 +109,8 @@
                       create<ast::CallStatement>(Source{}, call_expr),
                       create<ast::ReturnStatement>(Source{}),
                   });
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(caller),
-                                 caller, ast::VariableList(), void_type(), body,
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(caller), caller,
+                                 ast::VariableList(), ty.void_, body,
                                  decorations);
   }
 
@@ -125,7 +126,7 @@
           create<ast::Variable>(Source{},                   // source
                                 in,                         // name
                                 ast::StorageClass::kInput,  // storage_class
-                                u32_type(),                 // type
+                                ty.u32,                     // type
                                 false,                      // is_const
                                 nullptr,                    // constructor
                                 ast::VariableDecorationList{});  // decorations
@@ -133,12 +134,12 @@
           create<ast::Variable>(Source{},                    // source
                                 out,                         // name
                                 ast::StorageClass::kOutput,  // storage_class
-                                u32_type(),                  // type
+                                ty.u32,                      // type
                                 false,                       // is_const
                                 nullptr,                     // constructor
                                 ast::VariableDecorationList{});  // decorations
-      mod()->AddGlobalVariable(in_var);
-      mod()->AddGlobalVariable(out_var);
+      mod->AddGlobalVariable(in_var);
+      mod->AddGlobalVariable(out_var);
     }
   }
 
@@ -158,15 +159,15 @@
       std::tie(in, out) = inout;
       stmts.emplace_back(create<ast::AssignmentStatement>(
           Source{},
-          create<ast::IdentifierExpression>(Source{},
-                                            mod()->RegisterSymbol(out), out),
-          create<ast::IdentifierExpression>(Source{}, mod()->RegisterSymbol(in),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol(out),
+                                            out),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol(in),
                                             in)));
     }
     stmts.emplace_back(create<ast::ReturnStatement>(Source{}));
     auto* body = create<ast::BlockStatement>(Source{}, stmts);
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(name), name,
-                                 ast::VariableList(), void_type(), body,
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(name), name,
+                                 ast::VariableList(), ty.void_, body,
                                  decorations);
   }
 
@@ -189,20 +190,20 @@
       std::tie(in, out) = inout;
       stmts.emplace_back(create<ast::AssignmentStatement>(
           Source{},
-          create<ast::IdentifierExpression>(Source{},
-                                            mod()->RegisterSymbol(out), out),
-          create<ast::IdentifierExpression>(Source{}, mod()->RegisterSymbol(in),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol(out),
+                                            out),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol(in),
                                             in)));
     }
     auto* ident_expr = create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(callee), callee);
+        Source{}, mod->RegisterSymbol(callee), callee);
     auto* call_expr = create<ast::CallExpression>(Source{}, ident_expr,
                                                   ast::ExpressionList());
     stmts.emplace_back(create<ast::CallStatement>(Source{}, call_expr));
     stmts.emplace_back(create<ast::ReturnStatement>(Source{}));
     auto* body = create<ast::BlockStatement>(Source{}, stmts);
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(caller),
-                                 caller, ast::VariableList(), void_type(), body,
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(caller), caller,
+                                 ast::VariableList(), ty.void_, body,
                                  decorations);
   }
 
@@ -233,7 +234,7 @@
             // decorations
             create<ast::ConstantIdDecoration>(Source{}, id),
         });
-    mod()->AddGlobalVariable(var);
+    mod->AddGlobalVariable(var);
   }
 
   /// @param type AST type of the literal, must resolve to BoolLiteral
@@ -301,14 +302,8 @@
       uint32_t offset;
       std::tie(type, offset) = member_info;
 
-      ast::StructMemberDecorationList deco;
-      deco.push_back(
-          create<ast::StructMemberOffsetDecoration>(Source{}, offset));
-
-      auto member_name = StructMemberName(members.size(), type);
-      members.push_back(create<ast::StructMember>(
-          Source{}, mod()->RegisterSymbol(member_name), member_name, type,
-          deco));
+      members.push_back(Member(StructMemberName(members.size(), type), type,
+                               {MemberOffset(offset)}));
     }
 
     ast::StructDecorationList decos;
@@ -318,8 +313,8 @@
 
     auto* str = create<ast::Struct>(Source{}, members, decos);
 
-    return std::make_unique<ast::type::Struct>(mod()->RegisterSymbol(name),
-                                               name, str);
+    return std::make_unique<ast::type::Struct>(mod->RegisterSymbol(name), name,
+                                               str);
   }
 
   /// Generates types appropriate for using in an uniform buffer
@@ -400,7 +395,7 @@
             create<ast::SetDecoration>(Source{}, set),
         });
 
-    mod()->AddGlobalVariable(var);
+    mod->AddGlobalVariable(var);
   }
 
   /// Adds an uniform buffer variable to the module
@@ -461,21 +456,21 @@
       stmts.emplace_back(create<ast::AssignmentStatement>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod()->RegisterSymbol("local" + member_name),
+              Source{}, mod->RegisterSymbol("local" + member_name),
               "local" + member_name),
           create<ast::MemberAccessorExpression>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod()->RegisterSymbol(struct_name), struct_name),
+                  Source{}, mod->RegisterSymbol(struct_name), struct_name),
               create<ast::IdentifierExpression>(
-                  Source{}, mod()->RegisterSymbol(member_name), member_name))));
+                  Source{}, mod->RegisterSymbol(member_name), member_name))));
     }
 
     stmts.emplace_back(create<ast::ReturnStatement>(Source{}));
     auto* body = create<ast::BlockStatement>(Source{}, stmts);
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(func_name),
-                                 func_name, ast::VariableList(), void_type(),
-                                 body, ast::FunctionDecorationList{});
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(func_name),
+                                 func_name, ast::VariableList(), ty.void_, body,
+                                 ast::FunctionDecorationList{});
   }
 
   /// Adds a regular sampler variable to the module
@@ -551,7 +546,7 @@
   }
 
   void AddGlobalVariable(const std::string& name, ast::type::Type* type) {
-    mod()->AddGlobalVariable(create<ast::Variable>(
+    mod->AddGlobalVariable(create<ast::Variable>(
         Source{},                             // source
         name,                                 // name
         ast::StorageClass::kUniformConstant,  // storage_class
@@ -565,7 +560,7 @@
   /// @param name the name of the variable
   /// @param type the type to use
   void AddDepthTexture(const std::string& name, ast::type::Type* type) {
-    mod()->AddGlobalVariable(create<ast::Variable>(
+    mod->AddGlobalVariable(create<ast::Variable>(
         Source{},                             // source
         name,                                 // name
         ast::StorageClass::kUniformConstant,  // storage_class
@@ -606,29 +601,28 @@
 
     ast::ExpressionList call_params;
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(texture_name), texture_name));
+        Source{}, mod->RegisterSymbol(texture_name), texture_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(sampler_name), sampler_name));
+        Source{}, mod->RegisterSymbol(sampler_name), sampler_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(coords_name), coords_name));
+        Source{}, mod->RegisterSymbol(coords_name), coords_name));
     auto* call_expr = create<ast::CallExpression>(
         Source{},
         create<ast::IdentifierExpression>(
-            Source{}, mod()->RegisterSymbol("textureSample"), "textureSample"),
+            Source{}, mod->RegisterSymbol("textureSample"), "textureSample"),
         call_params);
 
     stmts.emplace_back(create<ast::AssignmentStatement>(
         Source{},
         create<ast::IdentifierExpression>(
-            Source{}, mod()->RegisterSymbol("sampler_result"),
-            "sampler_result"),
+            Source{}, mod->RegisterSymbol("sampler_result"), "sampler_result"),
         call_expr));
     stmts.emplace_back(create<ast::ReturnStatement>(Source{}));
 
     auto* body = create<ast::BlockStatement>(Source{}, stmts);
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(func_name),
-                                 func_name, ast::VariableList(), void_type(),
-                                 body, decorations);
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(func_name),
+                                 func_name, ast::VariableList(), ty.void_, body,
+                                 decorations);
   }
 
   /// Generates a function that references a specific sampler variable
@@ -665,31 +659,30 @@
 
     ast::ExpressionList call_params;
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(texture_name), texture_name));
+        Source{}, mod->RegisterSymbol(texture_name), texture_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(sampler_name), sampler_name));
+        Source{}, mod->RegisterSymbol(sampler_name), sampler_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(coords_name), coords_name));
+        Source{}, mod->RegisterSymbol(coords_name), coords_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(array_index), array_index));
+        Source{}, mod->RegisterSymbol(array_index), array_index));
     auto* call_expr = create<ast::CallExpression>(
         Source{},
         create<ast::IdentifierExpression>(
-            Source{}, mod()->RegisterSymbol("textureSample"), "textureSample"),
+            Source{}, mod->RegisterSymbol("textureSample"), "textureSample"),
         call_params);
 
     stmts.emplace_back(create<ast::AssignmentStatement>(
         Source{},
         create<ast::IdentifierExpression>(
-            Source{}, mod()->RegisterSymbol("sampler_result"),
-            "sampler_result"),
+            Source{}, mod->RegisterSymbol("sampler_result"), "sampler_result"),
         call_expr));
     stmts.emplace_back(create<ast::ReturnStatement>(Source{}));
 
     auto* body = create<ast::BlockStatement>(Source{}, stmts);
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(func_name),
-                                 func_name, ast::VariableList(), void_type(),
-                                 body, decorations);
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(func_name),
+                                 func_name, ast::VariableList(), ty.void_, body,
+                                 decorations);
   }
 
   /// Generates a function that references a specific comparison sampler
@@ -727,32 +720,31 @@
 
     ast::ExpressionList call_params;
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(texture_name), texture_name));
+        Source{}, mod->RegisterSymbol(texture_name), texture_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(sampler_name), sampler_name));
+        Source{}, mod->RegisterSymbol(sampler_name), sampler_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(coords_name), coords_name));
+        Source{}, mod->RegisterSymbol(coords_name), coords_name));
     call_params.push_back(create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(depth_name), depth_name));
+        Source{}, mod->RegisterSymbol(depth_name), depth_name));
     auto* call_expr = create<ast::CallExpression>(
         Source{},
         create<ast::IdentifierExpression>(
-            Source{}, mod()->RegisterSymbol("textureSampleCompare"),
+            Source{}, mod->RegisterSymbol("textureSampleCompare"),
             "textureSampleCompare"),
         call_params);
 
     stmts.emplace_back(create<ast::AssignmentStatement>(
         Source{},
         create<ast::IdentifierExpression>(
-            Source{}, mod()->RegisterSymbol("sampler_result"),
-            "sampler_result"),
+            Source{}, mod->RegisterSymbol("sampler_result"), "sampler_result"),
         call_expr));
     stmts.emplace_back(create<ast::ReturnStatement>(Source{}));
 
     auto* body = create<ast::BlockStatement>(Source{}, stmts);
-    return create<ast::Function>(Source{}, mod()->RegisterSymbol(func_name),
-                                 func_name, ast::VariableList(), void_type(),
-                                 body, decorations);
+    return create<ast::Function>(Source{}, mod->RegisterSymbol(func_name),
+                                 func_name, ast::VariableList(), ty.void_, body,
+                                 decorations);
   }
 
   /// Gets an appropriate type for the data in a given texture type.
@@ -761,11 +753,11 @@
   ast::type::Type* GetBaseType(ResourceBinding::SampledKind sampled_kind) {
     switch (sampled_kind) {
       case ResourceBinding::SampledKind::kFloat:
-        return f32_type();
+        return ty.f32;
       case ResourceBinding::SampledKind::kSInt:
-        return i32_type();
+        return ty.i32;
       case ResourceBinding::SampledKind::kUInt:
-        return u32_type();
+        return ty.u32;
       default:
         return nullptr;
     }
@@ -790,18 +782,13 @@
     return vec_type(base_type, 3);
   }
 
-  ast::Module* mod() { return &mod_; }
   TypeDeterminer* td() { return td_.get(); }
   Inspector* inspector() { return inspector_.get(); }
 
-  ast::type::Bool* bool_type() { return &bool_type_; }
-  ast::type::F32* f32_type() { return &f32_type_; }
-  ast::type::I32* i32_type() { return &i32_type_; }
-  ast::type::U32* u32_type() { return &u32_type_; }
   ast::type::Array* u32_array_type(uint32_t count) {
     if (array_type_memo_.find(count) == array_type_memo_.end()) {
       array_type_memo_[count] = create<ast::type::Array>(
-          u32_type(), count,
+          ty.u32, count,
           ast::ArrayDecorationList{
               create<ast::StrideDecoration>(Source{}, 4),
           });
@@ -812,35 +799,19 @@
     if (vector_type_memo_.find(std::tie(type, count)) ==
         vector_type_memo_.end()) {
       vector_type_memo_[std::tie(type, count)] =
-          std::make_unique<ast::type::Vector>(u32_type(), count);
+          std::make_unique<ast::type::Vector>(ty.u32, count);
     }
     return vector_type_memo_[std::tie(type, count)].get();
   }
-  ast::type::Void* void_type() { return &void_type_; }
   ast::type::Sampler* sampler_type() { return &sampler_type_; }
   ast::type::Sampler* comparison_sampler_type() {
     return &comparison_sampler_type_;
   }
 
-  /// Creates a new `ast::Node` owned by the Module. When the Module is
-  /// destructed, the `ast::Node` will also be destructed.
-  /// @param args the arguments to pass to the type constructor
-  /// @returns the node pointer
-  template <typename T, typename... ARGS>
-  T* create(ARGS&&... args) {
-    return mod_.create<T>(std::forward<ARGS>(args)...);
-  }
-
  private:
-  ast::Module mod_;
   std::unique_ptr<TypeDeterminer> td_;
   std::unique_ptr<Inspector> inspector_;
 
-  ast::type::Bool bool_type_;
-  ast::type::F32 f32_type_;
-  ast::type::I32 i32_type_;
-  ast::type::U32 u32_type_;
-  ast::type::Void void_type_;
   ast::type::Sampler sampler_type_;
   ast::type::Sampler comparison_sampler_type_;
   std::map<uint32_t, ast::type::Array*> array_type_memo_;
@@ -905,7 +876,7 @@
 }
 
 TEST_F(InspectorGetEntryPointTest, NoEntryPoints) {
-  mod()->AddFunction(MakeEmptyBodyFunction("foo"));
+  mod->AddFunction(MakeEmptyBodyFunction("foo"));
 
   auto result = inspector()->GetEntryPoints();
   ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
@@ -919,7 +890,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   // TODO(dsinclair): Update to run the namer transform when available.
 
@@ -938,14 +909,14 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto* bar = MakeEmptyBodyFunction(
       "bar",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
-  mod()->AddFunction(bar);
+  mod->AddFunction(bar);
 
   // TODO(dsinclair): Update to run the namer transform when available.
 
@@ -963,21 +934,21 @@
 
 TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) {
   auto* func = MakeEmptyBodyFunction("func");
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   auto* foo = MakeCallerBodyFunction(
       "foo", "func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto* bar = MakeCallerBodyFunction(
       "bar", "func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
-  mod()->AddFunction(bar);
+  mod->AddFunction(bar);
 
   // TODO(dsinclair): Update to run the namer transform when available.
 
@@ -999,7 +970,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto result = inspector()->GetEntryPoints();
   ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
@@ -1019,7 +990,7 @@
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
           create<ast::WorkgroupDecoration>(Source{}, 8u, 2u, 1u),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto result = inspector()->GetEntryPoints();
   ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
@@ -1034,14 +1005,14 @@
 
 TEST_F(InspectorGetEntryPointTest, NoInOutVariables) {
   auto* func = MakeEmptyBodyFunction("func");
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   auto* foo = MakeCallerBodyFunction(
       "foo", "func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto result = inspector()->GetEntryPoints();
   ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
@@ -1059,7 +1030,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1078,14 +1049,14 @@
   AddInOutVariables({{"in_var", "out_var"}});
 
   auto* func = MakeInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   auto* foo = MakeCallerBodyFunction(
       "foo", "func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1104,14 +1075,14 @@
   AddInOutVariables({{"in_var", "out_var"}});
 
   auto* func = MakeInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   auto* foo = MakeInOutVariableCallerBodyFunction(
       "foo", "func", {{"in_var", "out_var"}},
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1134,7 +1105,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1156,14 +1127,14 @@
 
   auto* func = MakeInOutVariableBodyFunction(
       "func", {{"in_var", "out_var"}, {"in2_var", "out2_var"}});
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   auto* foo = MakeCallerBodyFunction(
       "foo", "func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1188,14 +1159,14 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto* bar = MakeInOutVariableBodyFunction(
       "bar", {{"in2_var", "out_var"}},
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
-  mod()->AddFunction(bar);
+  mod->AddFunction(bar);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1225,21 +1196,21 @@
   AddInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}});
 
   auto* func = MakeInOutVariableBodyFunction("func", {{"in2_var", "out2_var"}});
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   auto* foo = MakeInOutVariableCallerBodyFunction(
       "foo", "func", {{"in_var", "out_var"}},
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto* bar = MakeCallerBodyFunction(
       "bar", "func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
-  mod()->AddFunction(bar);
+  mod->AddFunction(bar);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1279,7 +1250,7 @@
 // TODO(rharrison): Reenable once GetRemappedNameForEntryPoint isn't a pass
 // through
 TEST_F(InspectorGetRemappedNameForEntryPointTest, DISABLED_NoEntryPoints) {
-  mod()->AddFunction(MakeEmptyBodyFunction("foo"));
+  mod->AddFunction(MakeEmptyBodyFunction("foo"));
 
   auto result = inspector()->GetRemappedNameForEntryPoint("foo");
   ASSERT_TRUE(inspector()->has_error());
@@ -1295,7 +1266,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   // TODO(dsinclair): Update to run the namer transform when available.
 
@@ -1314,7 +1285,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   // TODO(dsinclair): Update to run the namer transform when available.
 
@@ -1323,7 +1294,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
-  mod()->AddFunction(bar);
+  mod->AddFunction(bar);
 
   {
     auto result = inspector()->GetRemappedNameForEntryPoint("foo");
@@ -1340,9 +1311,9 @@
 TEST_F(InspectorGetConstantIDsTest, Bool) {
   bool val_true = true;
   bool val_false = false;
-  AddConstantID<bool>("foo", 1, bool_type(), nullptr);
-  AddConstantID<bool>("bar", 20, bool_type(), &val_true);
-  AddConstantID<bool>("baz", 300, bool_type(), &val_false);
+  AddConstantID<bool>("foo", 1, ty.bool_, nullptr);
+  AddConstantID<bool>("bar", 20, ty.bool_, &val_true);
+  AddConstantID<bool>("baz", 300, ty.bool_, &val_false);
 
   auto result = inspector()->GetConstantIDs();
   ASSERT_EQ(3u, result.size());
@@ -1361,8 +1332,8 @@
 
 TEST_F(InspectorGetConstantIDsTest, U32) {
   uint32_t val = 42;
-  AddConstantID<uint32_t>("foo", 1, u32_type(), nullptr);
-  AddConstantID<uint32_t>("bar", 20, u32_type(), &val);
+  AddConstantID<uint32_t>("foo", 1, ty.u32, nullptr);
+  AddConstantID<uint32_t>("bar", 20, ty.u32, &val);
 
   auto result = inspector()->GetConstantIDs();
   ASSERT_EQ(2u, result.size());
@@ -1378,9 +1349,9 @@
 TEST_F(InspectorGetConstantIDsTest, I32) {
   int32_t val_neg = -42;
   int32_t val_pos = 42;
-  AddConstantID<int32_t>("foo", 1, i32_type(), nullptr);
-  AddConstantID<int32_t>("bar", 20, i32_type(), &val_neg);
-  AddConstantID<int32_t>("baz", 300, i32_type(), &val_pos);
+  AddConstantID<int32_t>("foo", 1, ty.i32, nullptr);
+  AddConstantID<int32_t>("bar", 20, ty.i32, &val_neg);
+  AddConstantID<int32_t>("baz", 300, ty.i32, &val_pos);
 
   auto result = inspector()->GetConstantIDs();
   ASSERT_EQ(3u, result.size());
@@ -1401,10 +1372,10 @@
   float val_zero = 0.0f;
   float val_neg = -10.0f;
   float val_pos = 15.0f;
-  AddConstantID<float>("foo", 1, f32_type(), nullptr);
-  AddConstantID<float>("bar", 20, f32_type(), &val_zero);
-  AddConstantID<float>("baz", 300, f32_type(), &val_neg);
-  AddConstantID<float>("x", 4000, f32_type(), &val_pos);
+  AddConstantID<float>("foo", 1, ty.f32, nullptr);
+  AddConstantID<float>("bar", 20, ty.f32, &val_zero);
+  AddConstantID<float>("baz", 300, ty.f32, &val_neg);
+  AddConstantID<float>("x", 4000, ty.f32, &val_pos);
 
   auto result = inspector()->GetConstantIDs();
   ASSERT_EQ(4u, result.size());
@@ -1436,19 +1407,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) =
-      MakeUniformBufferTypes("foo_type", {{i32_type(), 0}});
+      MakeUniformBufferTypes("foo_type", {{ty.i32, 0}});
   AddUniformBuffer("foo_ub", foo_control_type.get(), 0, 0);
 
   auto* ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(ub_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(ub_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "ub_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1458,32 +1429,27 @@
 }
 
 TEST_F(InspectorGetUniformBufferResourceBindingsTest, MissingBlockDeco) {
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList deco;
-  deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-
-  auto name = StructMemberName(members.size(), i32_type());
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod()->RegisterSymbol(name), name, i32_type(), deco));
-
   ast::StructDecorationList decos;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{
+          Member(StructMemberName(0, ty.i32), ty.i32, {MemberOffset(0)})},
+      decos);
 
-  auto* str = create<ast::Struct>(Source{}, members, decos);
   auto foo_type = std::make_unique<ast::type::Struct>(
-      mod()->RegisterSymbol("foo_type"), "foo_type", str);
+      mod->RegisterSymbol("foo_type"), "foo_type", str);
 
   AddUniformBuffer("foo_ub", foo_type.get(), 0, 0);
 
   auto* ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(ub_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(ub_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "ub_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1496,19 +1462,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) =
-      MakeUniformBufferTypes("foo_type", {{i32_type(), 0}});
+      MakeUniformBufferTypes("foo_type", {{ty.i32, 0}});
   AddUniformBuffer("foo_ub", foo_control_type.get(), 0, 0);
 
   auto* ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(ub_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(ub_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "ub_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1525,19 +1491,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}});
+      "foo_type", {{ty.i32, 0}, {ty.u32, 4}, {ty.f32, 8}});
   AddUniformBuffer("foo_ub", foo_control_type.get(), 0, 0);
 
   auto* ub_func = MakeStructVariableReferenceBodyFunction(
-      "ub_func", "foo_ub", {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}});
-  mod()->AddFunction(ub_func);
+      "ub_func", "foo_ub", {{0, ty.i32}, {1, ty.u32}, {2, ty.f32}});
+  mod->AddFunction(ub_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "ub_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1554,7 +1520,7 @@
   std::unique_ptr<ast::type::Struct> ub_struct_type;
   std::unique_ptr<ast::type::AccessControl> ub_control_type;
   std::tie(ub_struct_type, ub_control_type) = MakeUniformBufferTypes(
-      "ub_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}});
+      "ub_type", {{ty.i32, 0}, {ty.u32, 4}, {ty.f32, 8}});
   AddUniformBuffer("ub_foo", ub_control_type.get(), 0, 0);
   AddUniformBuffer("ub_bar", ub_control_type.get(), 0, 1);
   AddUniformBuffer("ub_baz", ub_control_type.get(), 2, 0);
@@ -1562,9 +1528,8 @@
   auto AddReferenceFunc = [this](const std::string& func_name,
                                  const std::string& var_name) {
     auto* ub_func = MakeStructVariableReferenceBodyFunction(
-        func_name, var_name,
-        {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}});
-    mod()->AddFunction(ub_func);
+        func_name, var_name, {{0, ty.i32}, {1, ty.u32}, {2, ty.f32}});
+    mod->AddFunction(ub_func);
   };
   AddReferenceFunc("ub_foo_func", "ub_foo");
   AddReferenceFunc("ub_bar_func", "ub_bar");
@@ -1572,7 +1537,7 @@
 
   auto FuncCall = [&](const std::string& callee) {
     auto* ident_expr = create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(callee), callee);
+        Source{}, mod->RegisterSymbol(callee), callee);
     auto* call_expr = create<ast::CallExpression>(Source{}, ident_expr,
                                                   ast::ExpressionList());
     return create<ast::CallStatement>(Source{}, call_expr);
@@ -1586,12 +1551,12 @@
                 });
 
   ast::Function* func = create<ast::Function>(
-      Source{}, mod()->RegisterSymbol("ep_func"), "ep_func",
-      ast::VariableList(), void_type(), body,
+      Source{}, mod->RegisterSymbol("ep_func"), "ep_func", ast::VariableList(),
+      ty.void_, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1615,20 +1580,20 @@
 TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) {
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
-  std::tie(foo_struct_type, foo_control_type) = MakeUniformBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}});
+  std::tie(foo_struct_type, foo_control_type) =
+      MakeUniformBufferTypes("foo_type", {{ty.i32, 0}, {u32_array_type(4), 4}});
   AddUniformBuffer("foo_ub", foo_control_type.get(), 0, 0);
 
   auto* ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(ub_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(ub_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "ub_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1645,19 +1610,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) =
-      MakeStorageBufferTypes("foo_type", {{i32_type(), 0}});
+      MakeStorageBufferTypes("foo_type", {{ty.i32, 0}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1674,19 +1639,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}});
+      "foo_type", {{ty.i32, 0}, {ty.u32, 4}, {ty.f32, 8}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction(
-      "sb_func", "foo_sb", {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}});
-  mod()->AddFunction(sb_func);
+      "sb_func", "foo_sb", {{0, ty.i32}, {1, ty.u32}, {2, ty.f32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1703,7 +1668,7 @@
   std::unique_ptr<ast::type::Struct> sb_struct_type;
   std::unique_ptr<ast::type::AccessControl> sb_control_type;
   std::tie(sb_struct_type, sb_control_type) = MakeStorageBufferTypes(
-      "sb_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}});
+      "sb_type", {{ty.i32, 0}, {ty.u32, 4}, {ty.f32, 8}});
   AddStorageBuffer("sb_foo", sb_control_type.get(), 0, 0);
   AddStorageBuffer("sb_bar", sb_control_type.get(), 0, 1);
   AddStorageBuffer("sb_baz", sb_control_type.get(), 2, 0);
@@ -1711,9 +1676,8 @@
   auto AddReferenceFunc = [this](const std::string& func_name,
                                  const std::string& var_name) {
     auto* sb_func = MakeStructVariableReferenceBodyFunction(
-        func_name, var_name,
-        {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}});
-    mod()->AddFunction(sb_func);
+        func_name, var_name, {{0, ty.i32}, {1, ty.u32}, {2, ty.f32}});
+    mod->AddFunction(sb_func);
   };
   AddReferenceFunc("sb_foo_func", "sb_foo");
   AddReferenceFunc("sb_bar_func", "sb_bar");
@@ -1721,7 +1685,7 @@
 
   auto FuncCall = [&](const std::string& callee) {
     auto* ident_expr = create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(callee), callee);
+        Source{}, mod->RegisterSymbol(callee), callee);
     auto* call_expr = create<ast::CallExpression>(Source{}, ident_expr,
                                                   ast::ExpressionList());
     return create<ast::CallStatement>(Source{}, call_expr);
@@ -1735,12 +1699,12 @@
                 });
 
   ast::Function* func = create<ast::Function>(
-      Source{}, mod()->RegisterSymbol("ep_func"), "ep_func",
-      ast::VariableList(), void_type(), body,
+      Source{}, mod->RegisterSymbol("ep_func"), "ep_func", ast::VariableList(),
+      ty.void_, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1764,20 +1728,20 @@
 TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) {
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
-  std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}});
+  std::tie(foo_struct_type, foo_control_type) =
+      MakeStorageBufferTypes("foo_type", {{ty.i32, 0}, {u32_array_type(4), 4}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1793,20 +1757,20 @@
 TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) {
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
-  std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_array_type(0), 4}});
+  std::tie(foo_struct_type, foo_control_type) =
+      MakeStorageBufferTypes("foo_type", {{ty.i32, 0}, {u32_array_type(0), 4}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1823,19 +1787,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) =
-      MakeReadOnlyStorageBufferTypes("foo_type", {{i32_type(), 0}});
+      MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32, 0}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1848,19 +1812,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) =
-      MakeReadOnlyStorageBufferTypes("foo_type", {{i32_type(), 0}});
+      MakeReadOnlyStorageBufferTypes("foo_type", {{ty.i32, 0}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1879,7 +1843,7 @@
   std::unique_ptr<ast::type::Struct> sb_struct_type;
   std::unique_ptr<ast::type::AccessControl> sb_control_type;
   std::tie(sb_struct_type, sb_control_type) = MakeReadOnlyStorageBufferTypes(
-      "sb_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}});
+      "sb_type", {{ty.i32, 0}, {ty.u32, 4}, {ty.f32, 8}});
   AddStorageBuffer("sb_foo", sb_control_type.get(), 0, 0);
   AddStorageBuffer("sb_bar", sb_control_type.get(), 0, 1);
   AddStorageBuffer("sb_baz", sb_control_type.get(), 2, 0);
@@ -1887,9 +1851,8 @@
   auto AddReferenceFunc = [this](const std::string& func_name,
                                  const std::string& var_name) {
     auto* sb_func = MakeStructVariableReferenceBodyFunction(
-        func_name, var_name,
-        {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}});
-    mod()->AddFunction(sb_func);
+        func_name, var_name, {{0, ty.i32}, {1, ty.u32}, {2, ty.f32}});
+    mod->AddFunction(sb_func);
   };
   AddReferenceFunc("sb_foo_func", "sb_foo");
   AddReferenceFunc("sb_bar_func", "sb_bar");
@@ -1897,7 +1860,7 @@
 
   auto FuncCall = [&](const std::string& callee) {
     auto* ident_expr = create<ast::IdentifierExpression>(
-        Source{}, mod()->RegisterSymbol(callee), callee);
+        Source{}, mod->RegisterSymbol(callee), callee);
     auto* call_expr = create<ast::CallExpression>(Source{}, ident_expr,
                                                   ast::ExpressionList());
     return create<ast::CallStatement>(Source{}, call_expr);
@@ -1911,12 +1874,12 @@
                 });
 
   ast::Function* func = create<ast::Function>(
-      Source{}, mod()->RegisterSymbol("ep_func"), "ep_func",
-      ast::VariableList(), void_type(), body,
+      Source{}, mod->RegisterSymbol("ep_func"), "ep_func", ast::VariableList(),
+      ty.void_, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1942,19 +1905,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}});
+      "foo_type", {{ty.i32, 0}, {u32_array_type(4), 4}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -1973,19 +1936,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) = MakeReadOnlyStorageBufferTypes(
-      "foo_type", {{i32_type(), 0}, {u32_array_type(0), 4}});
+      "foo_type", {{ty.i32, 0}, {u32_array_type(0), 4}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2003,19 +1966,19 @@
   std::unique_ptr<ast::type::Struct> foo_struct_type;
   std::unique_ptr<ast::type::AccessControl> foo_control_type;
   std::tie(foo_struct_type, foo_control_type) =
-      MakeStorageBufferTypes("foo_type", {{i32_type(), 0}});
+      MakeStorageBufferTypes("foo_type", {{ty.i32, 0}});
   AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0);
 
   auto* sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
-                                                          {{0, i32_type()}});
-  mod()->AddFunction(sb_func);
+                                                          {{0, ty.i32}});
+  mod->AddFunction(sb_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "sb_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2027,17 +1990,17 @@
 
 TEST_F(InspectorGetSamplerResourceBindingsTest, Simple) {
   auto sampled_texture_type =
-      MakeSampledTextureType(ast::type::TextureDimension::k1d, f32_type());
+      MakeSampledTextureType(ast::type::TextureDimension::k1d, ty.f32);
   AddSampledTexture("foo_texture", sampled_texture_type.get(), 0, 0);
   AddSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
 
   auto* func = MakeSamplerReferenceBodyFunction(
-      "ep", "foo_texture", "foo_sampler", "foo_coords", f32_type(),
+      "ep", "foo_texture", "foo_sampler", "foo_coords", ty.f32,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2055,7 +2018,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2067,21 +2030,21 @@
 
 TEST_F(InspectorGetSamplerResourceBindingsTest, InFunction) {
   auto sampled_texture_type =
-      MakeSampledTextureType(ast::type::TextureDimension::k1d, f32_type());
+      MakeSampledTextureType(ast::type::TextureDimension::k1d, ty.f32);
   AddSampledTexture("foo_texture", sampled_texture_type.get(), 0, 0);
   AddSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
 
   auto* foo_func = MakeSamplerReferenceBodyFunction(
-      "foo_func", "foo_texture", "foo_sampler", "foo_coords", f32_type());
-  mod()->AddFunction(foo_func);
+      "foo_func", "foo_texture", "foo_sampler", "foo_coords", ty.f32);
+  mod->AddFunction(foo_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "foo_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2095,17 +2058,17 @@
 
 TEST_F(InspectorGetSamplerResourceBindingsTest, UnknownEntryPoint) {
   auto sampled_texture_type =
-      MakeSampledTextureType(ast::type::TextureDimension::k1d, f32_type());
+      MakeSampledTextureType(ast::type::TextureDimension::k1d, ty.f32);
   AddSampledTexture("foo_texture", sampled_texture_type.get(), 0, 0);
   AddSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
 
   auto* func = MakeSamplerReferenceBodyFunction(
-      "ep", "foo_texture", "foo_sampler", "foo_coords", f32_type(),
+      "ep", "foo_texture", "foo_sampler", "foo_coords", ty.f32,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2118,15 +2081,15 @@
       MakeDepthTextureType(ast::type::TextureDimension::k2d);
   AddDepthTexture("foo_texture", depth_texture_type.get());
   AddComparisonSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
-  AddGlobalVariable("foo_depth", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
+  AddGlobalVariable("foo_depth", ty.f32);
 
   auto* func = MakeComparisonSamplerReferenceBodyFunction(
-      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_depth", f32_type(),
+      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_depth", ty.f32,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2141,15 +2104,15 @@
       MakeDepthTextureType(ast::type::TextureDimension::k2d);
   AddDepthTexture("foo_texture", depth_texture_type.get());
   AddComparisonSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
-  AddGlobalVariable("foo_depth", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
+  AddGlobalVariable("foo_depth", ty.f32);
 
   auto* func = MakeComparisonSamplerReferenceBodyFunction(
-      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_depth", f32_type(),
+      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_depth", ty.f32,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2167,7 +2130,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2182,20 +2145,20 @@
       MakeDepthTextureType(ast::type::TextureDimension::k2d);
   AddDepthTexture("foo_texture", depth_texture_type.get());
   AddComparisonSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
-  AddGlobalVariable("foo_depth", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
+  AddGlobalVariable("foo_depth", ty.f32);
 
   auto* foo_func = MakeComparisonSamplerReferenceBodyFunction(
       "foo_func", "foo_texture", "foo_sampler", "foo_coords", "foo_depth",
-      f32_type());
-  mod()->AddFunction(foo_func);
+      ty.f32);
+  mod->AddFunction(foo_func);
 
   auto* ep_func = MakeCallerBodyFunction(
       "ep_func", "foo_func",
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(ep_func);
+  mod->AddFunction(ep_func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2212,15 +2175,15 @@
       MakeDepthTextureType(ast::type::TextureDimension::k2d);
   AddDepthTexture("foo_texture", depth_texture_type.get());
   AddComparisonSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
-  AddGlobalVariable("foo_depth", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
+  AddGlobalVariable("foo_depth", ty.f32);
 
   auto* func = MakeComparisonSamplerReferenceBodyFunction(
-      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_depth", f32_type(),
+      "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_depth", ty.f32,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2230,17 +2193,17 @@
 
 TEST_F(InspectorGetComparisonSamplerResourceBindingsTest, SkipsSamplers) {
   auto sampled_texture_type =
-      MakeSampledTextureType(ast::type::TextureDimension::k1d, f32_type());
+      MakeSampledTextureType(ast::type::TextureDimension::k1d, ty.f32);
   AddSampledTexture("foo_texture", sampled_texture_type.get(), 0, 0);
   AddSampler("foo_sampler", 0, 1);
-  AddGlobalVariable("foo_coords", f32_type());
+  AddGlobalVariable("foo_coords", ty.f32);
 
   auto* func = MakeSamplerReferenceBodyFunction(
-      "ep", "foo_texture", "foo_sampler", "foo_coords", f32_type(),
+      "ep", "foo_texture", "foo_sampler", "foo_coords", ty.f32,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2256,7 +2219,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto result = inspector()->GetSampledTextureResourceBindings("foo");
   ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
@@ -2279,7 +2242,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2355,7 +2318,7 @@
   auto* coord_type =
       GetCoordsType(GetParam().type_dim, GetParam().sampled_kind);
   AddGlobalVariable("foo_coords", coord_type);
-  AddGlobalVariable("foo_array_index", u32_type());
+  AddGlobalVariable("foo_array_index", ty.u32);
 
   auto* func = MakeSamplerReferenceBodyFunction(
       "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_array_index",
@@ -2363,7 +2326,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2434,7 +2397,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
@@ -2483,7 +2446,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(foo);
+  mod->AddFunction(foo);
 
   auto result = inspector()->GetSampledTextureResourceBindings("foo");
   ASSERT_FALSE(inspector()->has_error()) << inspector()->error();
@@ -2500,7 +2463,7 @@
   auto* coord_type =
       GetCoordsType(GetParam().type_dim, GetParam().sampled_kind);
   AddGlobalVariable("foo_coords", coord_type);
-  AddGlobalVariable("foo_array_index", u32_type());
+  AddGlobalVariable("foo_array_index", ty.u32);
 
   auto* func = MakeSamplerReferenceBodyFunction(
       "ep", "foo_texture", "foo_sampler", "foo_coords", "foo_array_index",
@@ -2508,7 +2471,7 @@
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
-  mod()->AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td()->Determine()) << td()->error();
 
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 3576802..a3abb7f 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -785,14 +785,10 @@
 }
 
 TEST_F(TypeDeterminerTest, Expr_MemberAccessor_Struct) {
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("first_member"), "first_member", ty.i32, decos));
-  members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("second_member"), "second_member", ty.f32, decos));
-
-  auto* strct = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* strct = create<ast::Struct>(
+      ast::StructMemberList{Member("first_member", ty.i32),
+                            Member("second_member", ty.f32)},
+      ast::StructDecorationList{});
 
   ast::type::Struct st(mod->RegisterSymbol("S"), "S", strct);
 
@@ -812,14 +808,10 @@
 }
 
 TEST_F(TypeDeterminerTest, Expr_MemberAccessor_Struct_Alias) {
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("first_member"), "first_member", ty.i32, decos));
-  members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("second_member"), "second_member", ty.f32, decos));
-
-  auto* strct = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* strct = create<ast::Struct>(
+      ast::StructMemberList{Member("first_member", ty.i32),
+                            Member("second_member", ty.f32)},
+      ast::StructDecorationList{});
 
   auto st = std::make_unique<ast::type::Struct>(mod->RegisterSymbol("alias"),
                                                 "alias", strct);
@@ -898,21 +890,14 @@
   // }
   //
 
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList b_members;
-  b_members.push_back(create<ast::StructMember>(mod->RegisterSymbol("foo"),
-                                                "foo", ty.vec4<f32>(), decos));
-
-  auto* strctB = create<ast::Struct>(b_members, ast::StructDecorationList{});
+  auto* strctB =
+      create<ast::Struct>(ast::StructMemberList{Member("foo", ty.vec4<f32>())},
+                          ast::StructDecorationList{});
   ast::type::Struct stB(mod->RegisterSymbol("B"), "B", strctB);
 
   ast::type::Vector vecB(&stB, 3);
-
-  ast::StructMemberList a_members;
-  a_members.push_back(create<ast::StructMember>(mod->RegisterSymbol("mem"),
-                                                "mem", &vecB, decos));
-
-  auto* strctA = create<ast::Struct>(a_members, ast::StructDecorationList{});
+  auto* strctA = create<ast::Struct>(
+      ast::StructMemberList{Member("mem", &vecB)}, ast::StructDecorationList{});
 
   ast::type::Struct stA(mod->RegisterSymbol("A"), "A", strctA);
 
diff --git a/src/validator/validator_type_test.cc b/src/validator/validator_type_test.cc
index 429145e..1bc997c 100644
--- a/src/validator/validator_type_test.cc
+++ b/src/validator/validator_type_test.cc
@@ -42,21 +42,13 @@
   //   rt: array<f32>;
   // };
 
-  ast::StructMemberList members;
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(create<ast::StructMember>(mod->RegisterSymbol("vf"), "vf",
-                                                ty.f32, deco));
-  }
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(create<ast::StructMember>(
-        Source{Source::Location{12, 34}}, mod->RegisterSymbol("rt"), "rt",
-        ty.array<f32>(), deco));
-  }
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>());
-  auto* st = create<ast::Struct>(members, decos);
+  auto* st =
+      create<ast::Struct>(ast::StructMemberList{Member("vf", ty.f32),
+                                                Member("rt", ty.array<f32>())},
+                          decos);
+
   ast::type::Struct struct_type(mod->RegisterSymbol("Foo"), "Foo", st);
 
   mod->AddConstructedType(&struct_type);
@@ -69,26 +61,18 @@
   //   rt: array<f32>;
   // };
 
-  ast::StructMemberList members;
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(create<ast::StructMember>(mod->RegisterSymbol("vf"), "vf",
-                                                ty.f32, deco));
-  }
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(create<ast::StructMember>(
-        Source{Source::Location{12, 34}}, mod->RegisterSymbol("rt"), "rt",
-        ty.array<f32>(), deco));
-  }
   ast::StructDecorationList decos;
-  auto* st = create<ast::Struct>(members, decos);
+  auto* st =
+      create<ast::Struct>(ast::StructMemberList{Member("vf", ty.f32),
+                                                Member("rt", ty.array<f32>())},
+                          decos);
+
   ast::type::Struct struct_type(mod->RegisterSymbol("Foo"), "Foo", st);
 
   mod->AddConstructedType(&struct_type);
   EXPECT_FALSE(v()->ValidateConstructedTypes(mod->constructed_types()));
   EXPECT_EQ(v()->error(),
-            "12:34 v-0031: a struct containing a runtime-sized array must be "
+            "v-0031: a struct containing a runtime-sized array must be "
             "in the 'storage' storage class: 'Foo'");
 }
 
@@ -99,27 +83,19 @@
   //   vf: f32;
   // };
 
-  ast::StructMemberList members;
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(create<ast::StructMember>(
-        Source{Source::Location{12, 34}}, mod->RegisterSymbol("rt"), "rt",
-        ty.array<f32>(), deco));
-  }
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(create<ast::StructMember>(mod->RegisterSymbol("vf"), "vf",
-                                                ty.f32, deco));
-  }
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>());
-  auto* st = create<ast::Struct>(members, decos);
+  auto* st =
+      create<ast::Struct>(ast::StructMemberList{Member("rt", ty.array<f32>()),
+                                                Member("vf", ty.f32)},
+                          decos);
+
   ast::type::Struct struct_type(mod->RegisterSymbol("Foo"), "Foo", st);
 
   mod->AddConstructedType(&struct_type);
   EXPECT_FALSE(v()->ValidateConstructedTypes(mod->constructed_types()));
   EXPECT_EQ(v()->error(),
-            "12:34 v-0015: runtime arrays may only appear as the last member "
+            "v-0015: runtime arrays may only appear as the last member "
             "of a struct: 'rt'");
 }
 
@@ -134,27 +110,16 @@
   ast::type::Alias alias{mod->RegisterSymbol("RTArr"), "RTArr",
                          ty.array<u32>()};
 
-  ast::StructMemberList members;
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(
-        create<ast::StructMember>(Source{Source::Location{12, 34}},
-                                  mod->RegisterSymbol("b"), "b", &alias, deco));
-  }
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(
-        create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.u32, deco));
-  }
-
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>());
-  auto* st = create<ast::Struct>(members, decos);
+  auto* st = create<ast::Struct>(
+      ast::StructMemberList{Member("b", &alias), Member("a", ty.u32)}, decos);
+
   ast::type::Struct struct_type(mod->RegisterSymbol("s"), "s", st);
   mod->AddConstructedType(&struct_type);
   EXPECT_FALSE(v()->ValidateConstructedTypes(mod->constructed_types()));
   EXPECT_EQ(v()->error(),
-            "12:34 v-0015: runtime arrays may only appear as the last member "
+            "v-0015: runtime arrays may only appear as the last member "
             "of a struct: 'b'");
 }
 
@@ -169,21 +134,11 @@
   ast::type::Alias alias{mod->RegisterSymbol("RTArr"), "RTArr",
                          ty.array<u32>()};
 
-  ast::StructMemberList members;
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(
-        create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.u32, deco));
-  }
-  {
-    ast::StructMemberDecorationList deco;
-    members.push_back(
-        create<ast::StructMember>(Source{Source::Location{12, 34}},
-                                  mod->RegisterSymbol("b"), "b", &alias, deco));
-  }
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>());
-  auto* st = create<ast::Struct>(members, decos);
+  auto* st = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.u32), Member("b", &alias)}, decos);
+
   ast::type::Struct struct_type(mod->RegisterSymbol("s"), "s", st);
   mod->AddConstructedType(&struct_type);
   EXPECT_TRUE(v()->ValidateConstructedTypes(mod->constructed_types()));
diff --git a/src/writer/hlsl/generator_impl_alias_type_test.cc b/src/writer/hlsl/generator_impl_alias_type_test.cc
index 8142c05..adf6b4c 100644
--- a/src/writer/hlsl/generator_impl_alias_type_test.cc
+++ b/src/writer/hlsl/generator_impl_alias_type_test.cc
@@ -16,8 +16,6 @@
 #include "src/ast/struct_member.h"
 #include "src/ast/struct_member_decoration.h"
 #include "src/ast/struct_member_offset_decoration.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/writer/hlsl/test_helper.h"
 
@@ -46,15 +44,8 @@
 
 TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_Struct) {
   auto* str = create<ast::Struct>(
-
-      ast::StructMemberList{
-          create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32,
-                                    ast::StructMemberDecorationList{}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.i32,
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(4)}),
-      },
+      ast::StructMemberList{Member("a", ty.f32),
+                            Member("b", ty.i32, {MemberOffset(4)})},
       ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("A"), "A", str);
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index c17e7dd..7ac56d4 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -260,12 +260,9 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_UniformStruct) {
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("coord"), "coord", ty.vec4<f32>(),
-      ast::StructMemberDecorationList{}));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("coord", ty.vec4<f32>())},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Uniforms"), "Uniforms", str);
 
@@ -316,18 +313,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer_Read) {
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
@@ -372,18 +361,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer_Read) {
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadOnly, &s);
@@ -429,18 +410,10 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_StorageBuffer_Store) {
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
@@ -969,16 +942,9 @@
   //   return;
   // }
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("d"), "d", ty.f32, a_deco));
-
-  ast::StructDecorationList s_decos;
-  s_decos.push_back(create<ast::StructBlockDecoration>());
-
-  auto* str = create<ast::Struct>(members, s_decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("d", ty.f32, {MemberOffset(0)})},
+      ast::StructDecorationList{create<ast::StructBlockDecoration>()});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc
index c79b12b..dee9662 100644
--- a/src/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -40,13 +40,9 @@
 using HlslGeneratorImplTest_MemberAccessor = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList deco;
-  deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("mem"), "mem",
-                                              ty.f32, deco));
-
-  auto* strct = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* strct = create<ast::Struct>(
+      ast::StructMemberList{Member("mem", ty.f32, {MemberOffset(0)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Str"), "Str", strct);
 
@@ -74,18 +70,11 @@
   //
   // -> asfloat(data.Load(4));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = MemberAccessor("data", "b");
@@ -112,18 +101,10 @@
   //
   // -> asint(data.Load(0));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = MemberAccessor("data", "a");
@@ -153,16 +134,8 @@
   //    data.Store3(4 + 16, asuint(_tint_tmp[1]));
 
   auto* str = create<ast::Struct>(
-      ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("z"), "z", ty.i32,
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("z"), "a", ty.mat2x3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(4)}),
-      },
+      ast::StructMemberList{Member("z", ty.i32, {MemberOffset(0)}),
+                            Member("a", ty.mat2x3<f32>(), {MemberOffset(4)})},
       ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
@@ -207,18 +180,10 @@
   //    data.Store3(4 + 0, asuint(_tint_tmp[0]);
   //    data.Store3(4 + 16, asuint(_tint_tmp[1]));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("z"), "z", ty.i32, a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.mat2x3<f32>(), b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("z", ty.i32, {MemberOffset(0)}),
+                            Member("a", ty.mat2x3<f32>(), {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
@@ -257,18 +222,10 @@
   // -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8),
   // data.Load2(4 + 16)));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("z"), "z", ty.i32, a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.mat3x2<f32>(), b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("z", ty.i32, {MemberOffset(0)}),
+                            Member("a", ty.mat3x2<f32>(), {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
@@ -304,18 +261,13 @@
   //
   // -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("z"), "z", ty.i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{
+          Member("z", ty.i32, {MemberOffset(0)}),
+          Member("a", ty.mat2x3<f32>(), {MemberOffset(4)}),
+      },
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.mat2x3<f32>(), b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = MemberAccessor("data", "a");
@@ -344,13 +296,10 @@
   // -> asfloat(uint3x3(data.Load3(0), data.Load3(16),
   // data.Load3(32)));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList deco;
-  deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.mat3x3<f32>(), deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.mat3x3<f32>(), {MemberOffset(0)})},
+      ast::StructDecorationList{});
 
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = MemberAccessor("data", "a");
@@ -379,18 +328,11 @@
   //
   // -> asfloat(data.Load((2 * 16) + (1 * 4) + 16)))
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("z"), "z", ty.i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("z", ty.i32, {MemberOffset(0)}),
+                            Member("a", ty.mat4x3<f32>(), {MemberOffset(16)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.mat4x3<f32>(), b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = IndexAccessor(
@@ -421,13 +363,9 @@
                            create<ast::StrideDecoration>(4),
                        });
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", &ary, a_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})},
+      ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = IndexAccessor(MemberAccessor("data", "a"), Expr(2));
@@ -457,13 +395,9 @@
                            create<ast::StrideDecoration>(4),
                        });
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", &ary, a_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})},
+      ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
   auto* expr = IndexAccessor(MemberAccessor("data", "a"),
@@ -491,18 +425,11 @@
   //
   // -> data.Store(0, asuint(2.0f));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
@@ -537,13 +464,9 @@
                            create<ast::StrideDecoration>(4),
                        });
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", &ary, a_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", &ary, {MemberOffset(0)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
@@ -576,18 +499,11 @@
   //
   // -> data.Store(0, asuint(2));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
@@ -618,18 +534,11 @@
   //
   // -> asfloat(data.Load(16));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.vec3<i32>(), a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+                            Member("b", ty.vec3<f32>(), {MemberOffset(16)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("b"), "b",
-                                              ty.vec3<f32>(), b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
@@ -657,18 +566,10 @@
   //
   // -> data.Store(16, asuint(float3(2.3f, 1.2f, 0.2f)));
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.vec3<i32>(), a_deco));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("b"), "b",
-                                              ty.vec3<f32>(), b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+                            Member("b", ty.vec3<f32>(), {MemberOffset(16)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
@@ -709,14 +610,8 @@
 
   auto* data_str = create<ast::Struct>(
       ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("a"), "a", ty.vec3<i32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.vec3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(16)}),
+          Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+          Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
       },
       ast::StructDecorationList{});
 
@@ -728,12 +623,7 @@
                        });
 
   auto* pre_str = create<ast::Struct>(
-      ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("c"), "c", &ary,
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-      },
+      ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
       ast::StructDecorationList{});
 
   ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
@@ -769,19 +659,10 @@
   //
   // -> asfloat(data.Load3(16 + (2 * 32))).xy
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList deco;
-
   auto* data_str = create<ast::Struct>(
       ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("a"), "a", ty.vec3<i32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.vec3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(16)}),
+          Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+          Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
       },
       ast::StructDecorationList{});
 
@@ -791,10 +672,7 @@
       &data, 4, ast::ArrayDecorationList{create<ast::StrideDecoration>(32)});
 
   auto* pre_str = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("c"), "c", &ary,
-          ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(0)})},
+      ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
       ast::StructDecorationList{});
 
   ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
@@ -834,14 +712,8 @@
 
   auto* data_str = create<ast::Struct>(
       ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("a"), "a", ty.vec3<i32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.vec3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(16)}),
+          Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+          Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
       },
       ast::StructDecorationList{});
 
@@ -853,10 +725,7 @@
                        });
 
   auto* pre_str = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("c"), "c", &ary,
-          ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(0)})},
+      ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
       ast::StructDecorationList{});
 
   ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
@@ -895,14 +764,8 @@
 
   auto* data_str = create<ast::Struct>(
       ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("a"), "a", ty.vec3<i32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.vec3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(16)}),
+          Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+          Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
       },
       ast::StructDecorationList{});
 
@@ -914,10 +777,7 @@
                        });
 
   auto* pre_str = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("c"), "c", &ary,
-          ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(0)})},
+      ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
       ast::StructDecorationList{});
 
   ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
@@ -956,14 +816,8 @@
 
   auto* data_str = create<ast::Struct>(
       ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("a"), "a", ty.vec3<i32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.vec3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(16)}),
+          Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+          Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
       },
       ast::StructDecorationList{});
 
@@ -975,10 +829,7 @@
                        });
 
   auto* pre_str = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("c"), "c", &ary,
-          ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(0)})},
+      ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
       ast::StructDecorationList{});
 
   ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
@@ -1021,14 +872,8 @@
 
   auto* data_str = create<ast::Struct>(
       ast::StructMemberList{
-          create<ast::StructMember>(
-              mod->RegisterSymbol("a"), "a", ty.vec3<i32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(0)}),
-          create<ast::StructMember>(
-              mod->RegisterSymbol("b"), "b", ty.vec3<f32>(),
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(16)}),
+          Member("a", ty.vec3<i32>(), {MemberOffset(0)}),
+          Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
       },
       ast::StructDecorationList{});
 
@@ -1040,10 +885,7 @@
                        });
 
   auto* pre_str = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("c"), "c", &ary,
-          ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(0)})},
+      ast::StructMemberList{Member("c", &ary, {MemberOffset(0)})},
       ast::StructDecorationList{});
 
   ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc
index 5870a65..6ca4983 100644
--- a/src/writer/hlsl/generator_impl_type_test.cc
+++ b/src/writer/hlsl/generator_impl_type_test.cc
@@ -132,17 +132,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
@@ -155,17 +148,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
@@ -174,22 +160,11 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
-  ast::StructMemberDecorationList decos;
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(4));
-
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(32));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(128));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("c"), "c", ty.f32, decos));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(4)}),
+                            Member("b", ty.f32, {MemberOffset(32)}),
+                            Member("c", ty.f32, {MemberOffset(128)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
@@ -205,16 +180,9 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("double"), "double", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("float"),
-                                              "float", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("double", ty.i32), Member("float", ty.f32)},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
@@ -228,20 +196,13 @@
 
 // TODO(dsinclair): How to translate [[block]]
 TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>());
 
-  auto* str = create<ast::Struct>(members, decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      decos);
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
diff --git a/src/writer/msl/generator_impl_alias_type_test.cc b/src/writer/msl/generator_impl_alias_type_test.cc
index 745c7e1..1cd8a4c 100644
--- a/src/writer/msl/generator_impl_alias_type_test.cc
+++ b/src/writer/msl/generator_impl_alias_type_test.cc
@@ -16,10 +16,6 @@
 #include "src/ast/module.h"
 #include "src/ast/struct.h"
 #include "src/ast/struct_member.h"
-#include "src/ast/struct_member_decoration.h"
-#include "src/ast/struct_member_offset_decoration.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
@@ -32,8 +28,7 @@
 using MslGeneratorImplTest = TestHelper;
 
 TEST_F(MslGeneratorImplTest, EmitConstructedType_F32) {
-  ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("a"), "a", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("a"), "a", ty.f32);
 
   ASSERT_TRUE(gen.EmitConstructedType(&alias)) << gen.error();
   EXPECT_EQ(gen.result(), R"(typedef float a;
@@ -41,8 +36,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructedType_NameCollision) {
-  ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("float"), "float", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("float"), "float", ty.f32);
 
   ASSERT_TRUE(gen.EmitConstructedType(&alias)) << gen.error();
   EXPECT_EQ(gen.result(), R"(typedef float float_tint_0;
@@ -50,23 +44,12 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructedType_Struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32),
+                            Member("b", ty.i32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"), "a", &f32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &i32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("a"), "a", str);
+  ast::type::Struct s(mod->RegisterSymbol("a"), "a", str);
 
   ASSERT_TRUE(gen.EmitConstructedType(&s)) << gen.error();
   EXPECT_EQ(gen.result(), R"(struct a {
@@ -77,24 +60,13 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructedType_AliasStructIdent) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32),
+                            Member("b", ty.i32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"), "a", &f32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &i32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("b"), "b", str);
-  ast::type::Alias alias(mod.RegisterSymbol("a"), "a", &s);
+  ast::type::Struct s(mod->RegisterSymbol("b"), "b", str);
+  ast::type::Alias alias(mod->RegisterSymbol("a"), "a", &s);
 
   ASSERT_TRUE(gen.EmitConstructedType(&alias)) << gen.error();
   EXPECT_EQ(gen.result(), R"(typedef b a;
diff --git a/src/writer/msl/generator_impl_array_accessor_test.cc b/src/writer/msl/generator_impl_array_accessor_test.cc
index f354506..541ab24 100644
--- a/src/writer/msl/generator_impl_array_accessor_test.cc
+++ b/src/writer/msl/generator_impl_array_accessor_test.cc
@@ -36,7 +36,7 @@
   auto* lit = create<ast::SintLiteral>(Source{}, &i32, 5);
   auto* idx = create<ast::ScalarConstructorExpression>(Source{}, lit);
   auto* ary = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("ary"), "ary");
+      Source{}, mod->RegisterSymbol("ary"), "ary");
 
   ast::ArrayAccessorExpression expr(Source{}, ary, idx);
 
@@ -46,9 +46,9 @@
 
 TEST_F(MslGeneratorImplTest, EmitArrayAccessor) {
   auto* ary = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("ary"), "ary");
+      Source{}, mod->RegisterSymbol("ary"), "ary");
   auto* idx = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("idx"), "idx");
+      Source{}, mod->RegisterSymbol("idx"), "idx");
 
   ast::ArrayAccessorExpression expr(Source{}, ary, idx);
 
diff --git a/src/writer/msl/generator_impl_assign_test.cc b/src/writer/msl/generator_impl_assign_test.cc
index 2db16f4..44cd4af 100644
--- a/src/writer/msl/generator_impl_assign_test.cc
+++ b/src/writer/msl/generator_impl_assign_test.cc
@@ -31,9 +31,9 @@
 
 TEST_F(MslGeneratorImplTest, Emit_Assign) {
   auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("lhs"), "lhs");
+      Source{}, mod->RegisterSymbol("lhs"), "lhs");
   auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("rhs"), "rhs");
+      Source{}, mod->RegisterSymbol("rhs"), "rhs");
   ast::AssignmentStatement assign(Source{}, lhs, rhs);
 
   gen.increment_indent();
diff --git a/src/writer/msl/generator_impl_binary_test.cc b/src/writer/msl/generator_impl_binary_test.cc
index 3b83f86..0855853 100644
--- a/src/writer/msl/generator_impl_binary_test.cc
+++ b/src/writer/msl/generator_impl_binary_test.cc
@@ -39,9 +39,9 @@
   auto params = GetParam();
 
   auto* left = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("left"), "left");
+      Source{}, mod->RegisterSymbol("left"), "left");
   auto* right = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("right"), "right");
+      Source{}, mod->RegisterSymbol("right"), "right");
 
   ast::BinaryExpression expr(Source{}, params.op, left, right);
 
diff --git a/src/writer/msl/generator_impl_bitcast_test.cc b/src/writer/msl/generator_impl_bitcast_test.cc
index 13f4c7f..fe8f8e5 100644
--- a/src/writer/msl/generator_impl_bitcast_test.cc
+++ b/src/writer/msl/generator_impl_bitcast_test.cc
@@ -32,7 +32,7 @@
 TEST_F(MslGeneratorImplTest, EmitExpression_Bitcast) {
   ast::type::F32 f32;
   auto* id = create<ast::IdentifierExpression>(Source{},
-                                               mod.RegisterSymbol("id"), "id");
+                                               mod->RegisterSymbol("id"), "id");
   ast::BitcastExpression bitcast(Source{}, &f32, id);
 
   ASSERT_TRUE(gen.EmitExpression(&bitcast)) << gen.error();
diff --git a/src/writer/msl/generator_impl_call_test.cc b/src/writer/msl/generator_impl_call_test.cc
index 85efb04..da88386 100644
--- a/src/writer/msl/generator_impl_call_test.cc
+++ b/src/writer/msl/generator_impl_call_test.cc
@@ -35,14 +35,14 @@
   ast::type::Void void_type;
 
   auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
+      Source{}, mod->RegisterSymbol("my_func"), "my_func");
   ast::CallExpression call(Source{}, id, {});
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
+      Source{}, mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{},
       &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(gen.EmitExpression(&call)) << gen.error();
   EXPECT_EQ(gen.result(), "my_func()");
@@ -52,19 +52,19 @@
   ast::type::Void void_type;
 
   auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
+      Source{}, mod->RegisterSymbol("my_func"), "my_func");
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param1"), "param1"));
+      Source{}, mod->RegisterSymbol("param1"), "param1"));
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param2"), "param2"));
+      Source{}, mod->RegisterSymbol("param2"), "param2"));
   ast::CallExpression call(Source{}, id, params);
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
+      Source{}, mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{},
       &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(gen.EmitExpression(&call)) << gen.error();
   EXPECT_EQ(gen.result(), "my_func(param1, param2)");
@@ -74,20 +74,20 @@
   ast::type::Void void_type;
 
   auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
+      Source{}, mod->RegisterSymbol("my_func"), "my_func");
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param1"), "param1"));
+      Source{}, mod->RegisterSymbol("param1"), "param1"));
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param2"), "param2"));
+      Source{}, mod->RegisterSymbol("param2"), "param2"));
   ast::CallStatement call(Source{},
                           create<ast::CallExpression>(Source{}, id, params));
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
+      Source{}, mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{},
       &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   gen.increment_indent();
   ASSERT_TRUE(gen.EmitStatement(&call)) << gen.error();
diff --git a/src/writer/msl/generator_impl_cast_test.cc b/src/writer/msl/generator_impl_cast_test.cc
index d8b44ae..a7f96a5 100644
--- a/src/writer/msl/generator_impl_cast_test.cc
+++ b/src/writer/msl/generator_impl_cast_test.cc
@@ -35,7 +35,7 @@
 
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("id"), "id"));
+      Source{}, mod->RegisterSymbol("id"), "id"));
 
   ast::TypeConstructorExpression cast(Source{}, &f32, params);
 
@@ -49,7 +49,7 @@
 
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("id"), "id"));
+      Source{}, mod->RegisterSymbol("id"), "id"));
 
   ast::TypeConstructorExpression cast(Source{}, &vec3, params);
 
diff --git a/src/writer/msl/generator_impl_function_entry_point_data_test.cc b/src/writer/msl/generator_impl_function_entry_point_data_test.cc
index d5b4535..fe17c53 100644
--- a/src/writer/msl/generator_impl_function_entry_point_data_test.cc
+++ b/src/writer/msl/generator_impl_function_entry_point_data_test.cc
@@ -77,8 +77,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -86,23 +86,23 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
+                            Source{}, mod->RegisterSymbol("foo"), "foo"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
+                            Source{}, mod->RegisterSymbol("bar"), "bar")),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("vtx_main"), "vtx_main", params, &f32, body,
+      Source{}, mod->RegisterSymbol("vtx_main"), "vtx_main", params, &f32, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -154,8 +154,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -163,23 +163,23 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
+                            Source{}, mod->RegisterSymbol("foo"), "foo"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
+                            Source{}, mod->RegisterSymbol("bar"), "bar")),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("vtx_main"), "vtx_main", params, &f32, body,
+      Source{}, mod->RegisterSymbol("vtx_main"), "vtx_main", params, &f32, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -231,8 +231,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -240,23 +240,23 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
+                            Source{}, mod->RegisterSymbol("foo"), "foo"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
+                            Source{}, mod->RegisterSymbol("bar"), "bar")),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      Source{}, mod->RegisterSymbol("main"), "main", params, &f32, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -308,8 +308,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -317,23 +317,23 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
+                            Source{}, mod->RegisterSymbol("foo"), "foo"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
+                            Source{}, mod->RegisterSymbol("bar"), "bar")),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      Source{}, mod->RegisterSymbol("main"), "main", params, &f32, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -382,8 +382,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -391,23 +391,23 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
+                            Source{}, mod->RegisterSymbol("foo"), "foo"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
+                            Source{}, mod->RegisterSymbol("bar"), "bar")),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      Source{}, mod->RegisterSymbol("main"), "main", params, &f32, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -451,8 +451,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -460,23 +460,23 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
+                            Source{}, mod->RegisterSymbol("foo"), "foo"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
+                            Source{}, mod->RegisterSymbol("bar"), "bar")),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      Source{}, mod->RegisterSymbol("main"), "main", params, &f32, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -526,31 +526,32 @@
   td.RegisterVariableForTesting(coord_var);
   td.RegisterVariableForTesting(depth_var);
 
-  mod.AddGlobalVariable(coord_var);
-  mod.AddGlobalVariable(depth_var);
+  mod->AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(depth_var);
 
   ast::VariableList params;
 
   auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("depth"), "depth"),
-                        create<ast::MemberAccessorExpression>(
-                            Source{},
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("coord"), "coord"),
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("x"), "x"))),
-                });
+      Source{},
+      ast::StatementList{
+          create<ast::AssignmentStatement>(
+              Source{},
+              create<ast::IdentifierExpression>(
+                  Source{}, mod->RegisterSymbol("depth"), "depth"),
+              create<ast::MemberAccessorExpression>(
+                  Source{},
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("coord"), "coord"),
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("x"), "x"))),
+      });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &void_type, body,
+      Source{}, mod->RegisterSymbol("main"), "main", params, &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc
index 9866ce2..b7e539e 100644
--- a/src/writer/msl/generator_impl_function_test.cc
+++ b/src/writer/msl/generator_impl_function_test.cc
@@ -63,11 +63,11 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
 
-  auto* func = create<ast::Function>(Source{}, mod.RegisterSymbol("my_func"),
+  auto* func = create<ast::Function>(Source{}, mod->RegisterSymbol("my_func"),
                                      "my_func", ast::VariableList{}, &void_type,
                                      body, ast::FunctionDecorationList{});
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
   gen.increment_indent();
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -88,11 +88,11 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
 
-  auto* func = create<ast::Function>(Source{}, mod.RegisterSymbol("main"),
+  auto* func = create<ast::Function>(Source{}, mod->RegisterSymbol("main"),
                                      "main", ast::VariableList{}, &void_type,
                                      body, ast::FunctionDecorationList{});
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
   gen.increment_indent();
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -134,11 +134,11 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
 
-  auto* func = create<ast::Function>(Source{}, mod.RegisterSymbol("my_func"),
+  auto* func = create<ast::Function>(Source{}, mod->RegisterSymbol("my_func"),
                                      "my_func", params, &void_type, body,
                                      ast::FunctionDecorationList{});
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
   gen.increment_indent();
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -182,8 +182,8 @@
   td.RegisterVariableForTesting(foo_var);
   td.RegisterVariableForTesting(bar_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
@@ -191,18 +191,18 @@
                     create<ast::AssignmentStatement>(
                         Source{},
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
+                            Source{}, mod->RegisterSymbol("bar"), "bar"),
                         create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
                     create<ast::ReturnStatement>(Source{}),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{create<ast::StageDecoration>(
           Source{}, ast::PipelineStage::kFragment)});
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -259,32 +259,33 @@
   td.RegisterVariableForTesting(coord_var);
   td.RegisterVariableForTesting(depth_var);
 
-  mod.AddGlobalVariable(coord_var);
-  mod.AddGlobalVariable(depth_var);
+  mod->AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(depth_var);
 
   ast::VariableList params;
   auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("depth"), "depth"),
-                        create<ast::MemberAccessorExpression>(
-                            Source{},
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("coord"), "coord"),
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("x"), "x"))),
-                    create<ast::ReturnStatement>(Source{}),
-                });
+      Source{},
+      ast::StatementList{
+          create<ast::AssignmentStatement>(
+              Source{},
+              create<ast::IdentifierExpression>(
+                  Source{}, mod->RegisterSymbol("depth"), "depth"),
+              create<ast::MemberAccessorExpression>(
+                  Source{},
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("coord"), "coord"),
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("x"), "x"))),
+          create<ast::ReturnStatement>(Source{}),
+      });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -324,7 +325,7 @@
 
   td.RegisterVariableForTesting(coord_var);
 
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ast::VariableList params;
   auto* var = create<ast::Variable>(
@@ -336,8 +337,8 @@
       create<ast::MemberAccessorExpression>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("coord"), "coord"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("x"),
+              Source{}, mod->RegisterSymbol("coord"), "coord"),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol("x"),
                                             "x")),  // constructor
       ast::VariableDecorationList{});               // decorations
 
@@ -347,13 +348,13 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -371,27 +372,16 @@
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer) {
   ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::I32 i32;
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"),
-                                              "a", &i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   auto* coord_var =
       create<ast::Variable>(Source{},                           // source
@@ -408,20 +398,20 @@
 
   td.RegisterVariableForTesting(coord_var);
 
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ast::VariableList params;
   auto* var = create<ast::Variable>(
       Source{},                      // source
       "v",                           // name
       ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
+      ty.f32,                        // type
       false,                         // is_const
       create<ast::MemberAccessorExpression>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("coord"), "coord"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
+              Source{}, mod->RegisterSymbol("coord"), "coord"),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol("b"),
                                             "b")),  // constructor
       ast::VariableDecorationList{});               // decorations
 
@@ -431,13 +421,13 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -459,27 +449,16 @@
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer) {
   ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::I32 i32;
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"),
-                                              "a", &i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadOnly, &s);
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   auto* coord_var =
       create<ast::Variable>(Source{},                           // source
@@ -496,7 +475,7 @@
 
   td.RegisterVariableForTesting(coord_var);
 
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ast::VariableList params;
 
@@ -504,13 +483,13 @@
       Source{},                      // source
       "v",                           // name
       ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
+      ty.f32,                        // type
       false,                         // is_const
       create<ast::MemberAccessorExpression>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("coord"), "coord"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
+              Source{}, mod->RegisterSymbol("coord"), "coord"),
+          create<ast::IdentifierExpression>(Source{}, mod->RegisterSymbol("b"),
                                             "b")),  // constructor
       ast::VariableDecorationList{});               // decorations
 
@@ -520,13 +499,13 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -592,9 +571,9 @@
   td.RegisterVariableForTesting(bar_var);
   td.RegisterVariableForTesting(val_var);
 
-  mod.AddGlobalVariable(foo_var);
-  mod.AddGlobalVariable(bar_var);
-  mod.AddGlobalVariable(val_var);
+  mod->AddGlobalVariable(foo_var);
+  mod->AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(val_var);
 
   ast::VariableList params;
   params.push_back(
@@ -612,24 +591,24 @@
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("bar"), "bar"),
+                  Source{}, mod->RegisterSymbol("bar"), "bar"),
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("foo"), "foo")),
+                  Source{}, mod->RegisterSymbol("foo"), "foo")),
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("val"), "val"),
+                  Source{}, mod->RegisterSymbol("val"), "val"),
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("param"), "param")),
+                  Source{}, mod->RegisterSymbol("param"), "param")),
           create<ast::ReturnStatement>(
               Source{}, create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
+                            Source{}, mod->RegisterSymbol("foo"), "foo")),
       });
   auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
+      Source{}, mod->RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
       ast::FunctionDecorationList{});
 
-  mod.AddFunction(sub_func);
+  mod->AddFunction(sub_func);
 
   ast::ExpressionList expr;
   expr.push_back(create<ast::ScalarConstructorExpression>(
@@ -641,21 +620,21 @@
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("bar"), "bar"),
+                  Source{}, mod->RegisterSymbol("bar"), "bar"),
               create<ast::CallExpression>(
                   Source{},
                   create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
+                      Source{}, mod->RegisterSymbol("sub_func"), "sub_func"),
                   expr)),
           create<ast::ReturnStatement>(Source{}),
       });
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      Source{}, mod->RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -706,7 +685,7 @@
 
   td.RegisterVariableForTesting(depth_var);
 
-  mod.AddGlobalVariable(depth_var);
+  mod->AddGlobalVariable(depth_var);
 
   ast::VariableList params;
   params.push_back(
@@ -723,13 +702,13 @@
       ast::StatementList{
           create<ast::ReturnStatement>(
               Source{}, create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("param"), "param")),
+                            Source{}, mod->RegisterSymbol("param"), "param")),
       });
   auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
+      Source{}, mod->RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
       ast::FunctionDecorationList{});
 
-  mod.AddFunction(sub_func);
+  mod->AddFunction(sub_func);
 
   ast::ExpressionList expr;
   expr.push_back(create<ast::ScalarConstructorExpression>(
@@ -741,22 +720,22 @@
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("depth"), "depth"),
+                  Source{}, mod->RegisterSymbol("depth"), "depth"),
               create<ast::CallExpression>(
                   Source{},
                   create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
+                      Source{}, mod->RegisterSymbol("sub_func"), "sub_func"),
                   expr)),
           create<ast::ReturnStatement>(Source{}),
       });
 
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      Source{}, mod->RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -814,8 +793,8 @@
   td.RegisterVariableForTesting(coord_var);
   td.RegisterVariableForTesting(depth_var);
 
-  mod.AddGlobalVariable(coord_var);
-  mod.AddGlobalVariable(depth_var);
+  mod->AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(depth_var);
 
   ast::VariableList params;
   params.push_back(
@@ -833,22 +812,22 @@
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("depth"), "depth"),
+                  Source{}, mod->RegisterSymbol("depth"), "depth"),
               create<ast::MemberAccessorExpression>(
                   Source{},
                   create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("coord"), "coord"),
+                      Source{}, mod->RegisterSymbol("coord"), "coord"),
                   create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("x"), "x"))),
+                      Source{}, mod->RegisterSymbol("x"), "x"))),
           create<ast::ReturnStatement>(
               Source{}, create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("param"), "param")),
+                            Source{}, mod->RegisterSymbol("param"), "param")),
       });
   auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
+      Source{}, mod->RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
       ast::FunctionDecorationList{});
 
-  mod.AddFunction(sub_func);
+  mod->AddFunction(sub_func);
 
   ast::ExpressionList expr;
   expr.push_back(create<ast::ScalarConstructorExpression>(
@@ -860,21 +839,21 @@
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("depth"), "depth"),
+                  Source{}, mod->RegisterSymbol("depth"), "depth"),
               create<ast::CallExpression>(
                   Source{},
                   create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
+                      Source{}, mod->RegisterSymbol("sub_func"), "sub_func"),
                   expr)),
           create<ast::ReturnStatement>(Source{}),
       });
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      Source{}, mod->RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -919,7 +898,7 @@
 
   td.RegisterVariableForTesting(coord_var);
 
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ast::VariableList params;
   params.push_back(
@@ -935,18 +914,19 @@
       Source{},
       ast::StatementList{
           create<ast::ReturnStatement>(
-              Source{}, create<ast::MemberAccessorExpression>(
-                            Source{},
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("coord"), "coord"),
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("x"), "x"))),
+              Source{},
+              create<ast::MemberAccessorExpression>(
+                  Source{},
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("coord"), "coord"),
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("x"), "x"))),
       });
   auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
+      Source{}, mod->RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
       ast::FunctionDecorationList{});
 
-  mod.AddFunction(sub_func);
+  mod->AddFunction(sub_func);
 
   ast::ExpressionList expr;
   expr.push_back(create<ast::ScalarConstructorExpression>(
@@ -961,7 +941,7 @@
       create<ast::CallExpression>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
+              Source{}, mod->RegisterSymbol("sub_func"), "sub_func"),
           expr),                       // constructor
       ast::VariableDecorationList{});  // decorations
 
@@ -972,13 +952,13 @@
                 });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -999,27 +979,16 @@
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_RW_StorageBuffer) {
   ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::I32 i32;
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"),
-                                              "a", &i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   auto* coord_var =
       create<ast::Variable>(Source{},                           // source
@@ -1035,14 +1004,14 @@
                             });
 
   td.RegisterVariableForTesting(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ast::VariableList params;
   params.push_back(
       create<ast::Variable>(Source{},                         // source
                             "param",                          // name
                             ast::StorageClass::kFunction,     // storage_class
-                            &f32,                             // type
+                            ty.f32,                           // type
                             false,                            // is_const
                             nullptr,                          // constructor
                             ast::VariableDecorationList{}));  // decorations
@@ -1051,33 +1020,34 @@
       Source{},
       ast::StatementList{
           create<ast::ReturnStatement>(
-              Source{}, create<ast::MemberAccessorExpression>(
-                            Source{},
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("coord"), "coord"),
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("b"), "b"))),
+              Source{},
+              create<ast::MemberAccessorExpression>(
+                  Source{},
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("coord"), "coord"),
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("b"), "b"))),
       });
   auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+      Source{}, mod->RegisterSymbol("sub_func"), "sub_func", params, ty.f32,
+      body, ast::FunctionDecorationList{});
 
-  mod.AddFunction(sub_func);
+  mod->AddFunction(sub_func);
 
   ast::ExpressionList expr;
   expr.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.0f)));
+      Source{}, create<ast::FloatLiteral>(Source{}, ty.f32, 1.0f)));
 
   auto* var = create<ast::Variable>(
       Source{},                      // source
       "v",                           // name
       ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
+      ty.f32,                        // type
       false,                         // is_const
       create<ast::CallExpression>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
+              Source{}, mod->RegisterSymbol("sub_func"), "sub_func"),
           expr),                       // constructor
       ast::VariableDecorationList{});  // decorations
 
@@ -1088,13 +1058,13 @@
                 });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -1121,27 +1091,16 @@
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_RO_StorageBuffer) {
   ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::I32 i32;
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"),
-                                              "a", &i32, a_deco));
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadOnly, &s);
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   auto* coord_var =
       create<ast::Variable>(Source{},                           // source
@@ -1157,14 +1116,14 @@
                             });
 
   td.RegisterVariableForTesting(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ast::VariableList params;
   params.push_back(
       create<ast::Variable>(Source{},                         // source
                             "param",                          // name
                             ast::StorageClass::kFunction,     // storage_class
-                            &f32,                             // type
+                            ty.f32,                           // type
                             false,                            // is_const
                             nullptr,                          // constructor
                             ast::VariableDecorationList{}));  // decorations
@@ -1173,33 +1132,34 @@
       Source{},
       ast::StatementList{
           create<ast::ReturnStatement>(
-              Source{}, create<ast::MemberAccessorExpression>(
-                            Source{},
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("coord"), "coord"),
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("b"), "b"))),
+              Source{},
+              create<ast::MemberAccessorExpression>(
+                  Source{},
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("coord"), "coord"),
+                  create<ast::IdentifierExpression>(
+                      Source{}, mod->RegisterSymbol("b"), "b"))),
       });
   auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+      Source{}, mod->RegisterSymbol("sub_func"), "sub_func", params, ty.f32,
+      body, ast::FunctionDecorationList{});
 
-  mod.AddFunction(sub_func);
+  mod->AddFunction(sub_func);
 
   ast::ExpressionList expr;
   expr.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.0f)));
+      Source{}, create<ast::FloatLiteral>(Source{}, ty.f32, 1.0f)));
 
   auto* var = create<ast::Variable>(
       Source{},                      // source
       "v",                           // name
       ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
+      ty.f32,                        // type
       false,                         // is_const
       create<ast::CallExpression>(
           Source{},
           create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
+              Source{}, mod->RegisterSymbol("sub_func"), "sub_func"),
           expr),                       // constructor
       ast::VariableDecorationList{});  // decorations
 
@@ -1210,13 +1170,13 @@
                 });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
+      Source{}, mod->RegisterSymbol("frag_main"), "frag_main", params,
       &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
@@ -1259,7 +1219,7 @@
                             });
 
   td.RegisterVariableForTesting(bar_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(bar_var);
 
   ast::VariableList params;
   auto* list = create<ast::BlockStatement>(
@@ -1273,7 +1233,7 @@
           create<ast::AssignmentStatement>(
               Source{},
               create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("bar"), "bar"),
+                  Source{}, mod->RegisterSymbol("bar"), "bar"),
               create<ast::ScalarConstructorExpression>(
                   Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.0f))),
           create<ast::IfStatement>(
@@ -1289,12 +1249,12 @@
       });
 
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      Source{}, mod->RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate()) << gen.error();
@@ -1321,13 +1281,13 @@
   ast::type::Void void_type;
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", ast::VariableList{},
+      Source{}, mod->RegisterSymbol("main"), "main", ast::VariableList{},
       &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
   EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
@@ -1359,11 +1319,11 @@
                     create<ast::ReturnStatement>(Source{}),
                 });
 
-  auto* func = create<ast::Function>(Source{}, mod.RegisterSymbol("my_func"),
+  auto* func = create<ast::Function>(Source{}, mod->RegisterSymbol("my_func"),
                                      "my_func", params, &void_type, body,
                                      ast::FunctionDecorationList{});
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   gen.increment_indent();
 
@@ -1396,20 +1356,14 @@
   // }
 
   ast::type::Void void_type;
-  ast::type::F32 f32;
-
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("d"),
-                                              "d", &f32, a_deco));
 
   ast::StructDecorationList s_decos;
   s_decos.push_back(create<ast::StructBlockDecoration>(Source{}));
 
-  auto* str = create<ast::Struct>(Source{}, members, s_decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("d", ty.f32, {MemberOffset(0)})}, s_decos);
 
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
 
   auto* data_var =
@@ -1425,10 +1379,10 @@
                                 create<ast::SetDecoration>(Source{}, 0),
                             });
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   td.RegisterVariableForTesting(data_var);
-  mod.AddGlobalVariable(data_var);
+  mod->AddGlobalVariable(data_var);
 
   {
     ast::VariableList params;
@@ -1436,13 +1390,14 @@
         Source{},                      // source
         "v",                           // name
         ast::StorageClass::kFunction,  // storage_class
-        &f32,                          // type
+        ty.f32,                        // type
         false,                         // is_const
         create<ast::MemberAccessorExpression>(
             Source{},
             create<ast::IdentifierExpression>(
-                Source{}, mod.RegisterSymbol("data"), "data"),
-            create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"),
+                Source{}, mod->RegisterSymbol("data"), "data"),
+            create<ast::IdentifierExpression>(Source{},
+                                              mod->RegisterSymbol("d"),
                                               "d")),  // constructor
         ast::VariableDecorationList{});               // decorations
 
@@ -1452,13 +1407,13 @@
                       create<ast::ReturnStatement>(Source{}),
                   });
     auto* func = create<ast::Function>(
-        Source{}, mod.RegisterSymbol("a"), "a", params, &void_type, body,
+        Source{}, mod->RegisterSymbol("a"), "a", params, &void_type, body,
         ast::FunctionDecorationList{
             create<ast::StageDecoration>(Source{},
                                          ast::PipelineStage::kCompute),
         });
 
-    mod.AddFunction(func);
+    mod->AddFunction(func);
   }
 
   {
@@ -1467,13 +1422,14 @@
         Source{},                      // source
         "v",                           // name
         ast::StorageClass::kFunction,  // storage_class
-        &f32,                          // type
+        ty.f32,                        // type
         false,                         // is_const
         create<ast::MemberAccessorExpression>(
             Source{},
             create<ast::IdentifierExpression>(
-                Source{}, mod.RegisterSymbol("data"), "data"),
-            create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"),
+                Source{}, mod->RegisterSymbol("data"), "data"),
+            create<ast::IdentifierExpression>(Source{},
+                                              mod->RegisterSymbol("d"),
                                               "d")),  // constructor
         ast::VariableDecorationList{});               // decorations
 
@@ -1483,13 +1439,13 @@
                       create<ast::ReturnStatement>(Source{}),
                   });
     auto* func = create<ast::Function>(
-        Source{}, mod.RegisterSymbol("b"), "b", params, &void_type, body,
+        Source{}, mod->RegisterSymbol("b"), "b", params, &void_type, body,
         ast::FunctionDecorationList{
             create<ast::StageDecoration>(Source{},
                                          ast::PipelineStage::kCompute),
         });
 
-    mod.AddFunction(func);
+    mod->AddFunction(func);
   }
 
   ASSERT_TRUE(td.Determine()) << td.error();
diff --git a/src/writer/msl/generator_impl_identifier_test.cc b/src/writer/msl/generator_impl_identifier_test.cc
index 4329b53..e203655 100644
--- a/src/writer/msl/generator_impl_identifier_test.cc
+++ b/src/writer/msl/generator_impl_identifier_test.cc
@@ -26,14 +26,14 @@
 using MslGeneratorImplTest = TestHelper;
 
 TEST_F(MslGeneratorImplTest, EmitIdentifierExpression) {
-  ast::IdentifierExpression i(Source{}, mod.RegisterSymbol("foo"), "foo");
+  ast::IdentifierExpression i(Source{}, mod->RegisterSymbol("foo"), "foo");
 
   ASSERT_TRUE(gen.EmitExpression(&i)) << gen.error();
   EXPECT_EQ(gen.result(), "foo");
 }
 
 TEST_F(MslGeneratorImplTest, EmitIdentifierExpression_Single_WithCollision) {
-  ast::IdentifierExpression i(Source{}, mod.RegisterSymbol("virtual"),
+  ast::IdentifierExpression i(Source{}, mod->RegisterSymbol("virtual"),
                               "virtual");
 
   ASSERT_TRUE(gen.EmitExpression(&i)) << gen.error();
diff --git a/src/writer/msl/generator_impl_if_test.cc b/src/writer/msl/generator_impl_if_test.cc
index 772dee9..7da3950 100644
--- a/src/writer/msl/generator_impl_if_test.cc
+++ b/src/writer/msl/generator_impl_if_test.cc
@@ -30,7 +30,7 @@
 
 TEST_F(MslGeneratorImplTest, Emit_If) {
   auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
+      Source{}, mod->RegisterSymbol("cond"), "cond");
   auto* body = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
                     create<ast::ReturnStatement>(Source{}),
@@ -48,14 +48,14 @@
 
 TEST_F(MslGeneratorImplTest, Emit_IfWithElseIf) {
   auto* else_cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("else_cond"), "else_cond");
+      Source{}, mod->RegisterSymbol("else_cond"), "else_cond");
   auto* else_body = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
                     create<ast::ReturnStatement>(Source{}),
                 });
 
   auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
+      Source{}, mod->RegisterSymbol("cond"), "cond");
   auto* body = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
                     create<ast::ReturnStatement>(Source{}),
@@ -82,7 +82,7 @@
                 });
 
   auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
+      Source{}, mod->RegisterSymbol("cond"), "cond");
   auto* body = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
                     create<ast::ReturnStatement>(Source{}),
@@ -104,7 +104,7 @@
 
 TEST_F(MslGeneratorImplTest, Emit_IfWithMultiple) {
   auto* else_cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("else_cond"), "else_cond");
+      Source{}, mod->RegisterSymbol("else_cond"), "else_cond");
 
   auto* else_body = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
@@ -117,7 +117,7 @@
                 });
 
   auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
+      Source{}, mod->RegisterSymbol("cond"), "cond");
   auto* body = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
                     create<ast::ReturnStatement>(Source{}),
diff --git a/src/writer/msl/generator_impl_import_test.cc b/src/writer/msl/generator_impl_import_test.cc
index e2b5c37..c27ee73 100644
--- a/src/writer/msl/generator_impl_import_test.cc
+++ b/src/writer/msl/generator_impl_import_test.cc
@@ -58,7 +58,7 @@
       Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
 
   auto* ident = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol(param.name), param.name);
+      Source{}, mod->RegisterSymbol(param.name), param.name);
 
   ast::CallExpression call(Source{}, ident, params);
 
@@ -103,7 +103,7 @@
 
   ast::CallExpression expr(Source{},
                            create<ast::IdentifierExpression>(
-                               Source{}, mod.RegisterSymbol("abs"), "abs"),
+                               Source{}, mod->RegisterSymbol("abs"), "abs"),
                            params);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
@@ -127,7 +127,7 @@
   ast::CallExpression expr(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
+          Source{}, mod->RegisterSymbol(param.name), param.name),
       params);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
@@ -180,7 +180,7 @@
   ast::CallExpression expr(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
+          Source{}, mod->RegisterSymbol(param.name), param.name),
       params);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
@@ -208,7 +208,7 @@
   ast::CallExpression expr(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
+          Source{}, mod->RegisterSymbol(param.name), param.name),
       params);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
@@ -237,7 +237,7 @@
   ast::CallExpression expr(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
+          Source{}, mod->RegisterSymbol(param.name), param.name),
       params);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
@@ -271,7 +271,7 @@
   ast::CallExpression expr(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
+          Source{}, mod->RegisterSymbol(param.name), param.name),
       params);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
@@ -299,15 +299,15 @@
 
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("var"), "var"));
+      Source{}, mod->RegisterSymbol("var"), "var"));
 
   ast::CallExpression expr(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol("determinant"), "determinant"),
+          Source{}, mod->RegisterSymbol("determinant"), "determinant"),
       params);
 
-  mod.AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td.Determine()) << td.error();
diff --git a/src/writer/msl/generator_impl_intrinsic_test.cc b/src/writer/msl/generator_impl_intrinsic_test.cc
index 28b7eae..ce36062 100644
--- a/src/writer/msl/generator_impl_intrinsic_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_test.cc
@@ -89,21 +89,21 @@
 
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("a"), "a"));
+      Source{}, mod->RegisterSymbol("a"), "a"));
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("b"), "b"));
+      Source{}, mod->RegisterSymbol("b"), "b"));
 
   ast::CallExpression call(
       Source{},
       create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol("outer_product"), "outer_product"),
+          Source{}, mod->RegisterSymbol("outer_product"), "outer_product"),
       params);
 
   td.RegisterVariableForTesting(a);
   td.RegisterVariableForTesting(b);
 
-  mod.AddGlobalVariable(a);
-  mod.AddGlobalVariable(b);
+  mod->AddGlobalVariable(a);
+  mod->AddGlobalVariable(b);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(td.DetermineResultType(&call)) << td.error();
@@ -123,13 +123,13 @@
 
   ast::ExpressionList params;
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param1"), "param1"));
+      Source{}, mod->RegisterSymbol("param1"), "param1"));
   params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param2"), "param2"));
+      Source{}, mod->RegisterSymbol("param2"), "param2"));
 
   ast::CallExpression call(Source{},
                            create<ast::IdentifierExpression>(
-                               Source{}, mod.RegisterSymbol("dot"), "dot"),
+                               Source{}, mod->RegisterSymbol("dot"), "dot"),
                            params);
 
   ast::Variable v1(Source{}, "param1", ast::StorageClass::kFunction, &vec,
diff --git a/src/writer/msl/generator_impl_loop_test.cc b/src/writer/msl/generator_impl_loop_test.cc
index b8b0a29..368c3dd 100644
--- a/src/writer/msl/generator_impl_loop_test.cc
+++ b/src/writer/msl/generator_impl_loop_test.cc
@@ -97,9 +97,9 @@
                                                });
 
   auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("lhs"), "lhs");
+      Source{}, mod->RegisterSymbol("lhs"), "lhs");
   auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("rhs"), "rhs");
+      Source{}, mod->RegisterSymbol("rhs"), "rhs");
 
   continuing = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
@@ -186,9 +186,9 @@
       });
 
   auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("lhs"), "lhs");
+      Source{}, mod->RegisterSymbol("lhs"), "lhs");
   auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("rhs"), "rhs");
+      Source{}, mod->RegisterSymbol("rhs"), "rhs");
 
   auto* continuing = create<ast::BlockStatement>(
       Source{}, ast::StatementList{
diff --git a/src/writer/msl/generator_impl_member_accessor_test.cc b/src/writer/msl/generator_impl_member_accessor_test.cc
index 2ad3397..e6b3d14 100644
--- a/src/writer/msl/generator_impl_member_accessor_test.cc
+++ b/src/writer/msl/generator_impl_member_accessor_test.cc
@@ -30,9 +30,9 @@
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor) {
   auto* str = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("str"), "str");
+      Source{}, mod->RegisterSymbol("str"), "str");
   auto* mem = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mem"), "mem");
+      Source{}, mod->RegisterSymbol("mem"), "mem");
 
   ast::MemberAccessorExpression expr(Source{}, str, mem);
 
diff --git a/src/writer/msl/generator_impl_return_test.cc b/src/writer/msl/generator_impl_return_test.cc
index 5efbfcc..8b8be3e 100644
--- a/src/writer/msl/generator_impl_return_test.cc
+++ b/src/writer/msl/generator_impl_return_test.cc
@@ -40,7 +40,7 @@
 
 TEST_F(MslGeneratorImplTest, Emit_ReturnWithValue) {
   auto* expr = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("expr"), "expr");
+      Source{}, mod->RegisterSymbol("expr"), "expr");
   ast::ReturnStatement r(Source{}, expr);
 
   gen.increment_indent();
diff --git a/src/writer/msl/generator_impl_switch_test.cc b/src/writer/msl/generator_impl_switch_test.cc
index eb7f766..c5bd20b 100644
--- a/src/writer/msl/generator_impl_switch_test.cc
+++ b/src/writer/msl/generator_impl_switch_test.cc
@@ -56,7 +56,7 @@
   body.push_back(def);
 
   auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
+      Source{}, mod->RegisterSymbol("cond"), "cond");
   ast::SwitchStatement s(Source{}, cond, body);
 
   gen.increment_indent();
diff --git a/src/writer/msl/generator_impl_test.cc b/src/writer/msl/generator_impl_test.cc
index b7e4e12..cf6df0e 100644
--- a/src/writer/msl/generator_impl_test.cc
+++ b/src/writer/msl/generator_impl_test.cc
@@ -51,12 +51,12 @@
   ast::type::Void void_type;
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
+      Source{}, mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{},
       &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
       ast::FunctionDecorationList{
           create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
       });
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
   EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
@@ -79,7 +79,7 @@
 TEST_F(MslGeneratorImplTest, NameConflictWith_InputStructName) {
   ASSERT_EQ(gen.generate_name("func_main_in"), "func_main_in");
 
-  ast::IdentifierExpression ident(Source{}, mod.RegisterSymbol("func_main_in"),
+  ast::IdentifierExpression ident(Source{}, mod->RegisterSymbol("func_main_in"),
                                   "func_main_in");
   ASSERT_TRUE(gen.EmitIdentifier(&ident));
   EXPECT_EQ(gen.result(), "func_main_in_0");
@@ -118,7 +118,7 @@
 
 TEST_F(MslGeneratorImplTest, calculate_alignment_size_alias) {
   ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("a"), "a", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("a"), "a", &f32);
   EXPECT_EQ(4u, gen.calculate_alignment_size(&alias));
 }
 
@@ -156,73 +156,33 @@
 }
 
 TEST_F(MslGeneratorImplTest, calculate_alignment_size_struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(4)}),
+                            Member("b", ty.f32, {MemberOffset(32)}),
+                            Member("c", ty.f32, {MemberOffset(128)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList decos;
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"),
-                                              "a", &i32, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 32));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 128));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("c"),
-                                              "c", &f32, decos));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   EXPECT_EQ(132u, gen.calculate_alignment_size(&s));
 }
 
 TEST_F(MslGeneratorImplTest, calculate_alignment_size_struct_of_struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-  ast::type::Vector fvec(&f32, 3);
+  auto* inner_str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32, {MemberOffset(0)}),
+                            Member("b", ty.vec3<f32>(), {MemberOffset(16)}),
+                            Member("c", ty.f32, {MemberOffset(32)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberDecorationList decos;
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
+  ast::type::Struct inner_s(mod->RegisterSymbol("Inner"), "Inner", inner_str);
 
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"),
-                                              "a", &i32, decos));
+  auto* outer_str = create<ast::Struct>(
+      ast::StructMemberList{Member("d", ty.f32, {MemberOffset(0)}),
+                            Member("e", &inner_s, {MemberOffset(32)}),
+                            Member("f", ty.f32, {MemberOffset(64)})},
+      ast::StructDecorationList{});
 
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 16));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &fvec, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 32));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("c"),
-                                              "c", &f32, decos));
-
-  auto* inner_str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct inner_s(mod.RegisterSymbol("Inner"), "Inner", inner_str);
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("d"),
-                                              "d", &f32, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 32));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("e"),
-                                              "e", &inner_s, decos));
-
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 64));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("f"),
-                                              "f", &f32, decos));
-
-  auto* outer_str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct outer_s(mod.RegisterSymbol("Outer"), "Outer", outer_str);
+  ast::type::Struct outer_s(mod->RegisterSymbol("Outer"), "Outer", outer_str);
 
   EXPECT_EQ(80u, gen.calculate_alignment_size(&outer_s));
 }
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc
index 62d5700..c2dd81c 100644
--- a/src/writer/msl/generator_impl_type_test.cc
+++ b/src/writer/msl/generator_impl_type_test.cc
@@ -48,7 +48,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_Alias) {
   ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("alias"), "alias", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("alias"), "alias", &f32);
 
   ASSERT_TRUE(gen.EmitType(&alias, "")) << gen.error();
   EXPECT_EQ(gen.result(), "alias");
@@ -56,7 +56,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_Alias_NameCollision) {
   ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("bool"), "bool", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("bool"), "bool", &f32);
 
   ASSERT_TRUE(gen.EmitType(&alias, "")) << gen.error();
   EXPECT_EQ(gen.result(), "bool_tint_0");
@@ -171,46 +171,24 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"), "a", &i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   ASSERT_TRUE(gen.EmitType(&s, "")) << gen.error();
   EXPECT_EQ(gen.result(), "S");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_StructDecl) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"), "a", &i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   ASSERT_TRUE(gen.EmitStructType(&s)) << gen.error();
   EXPECT_EQ(gen.result(), R"(struct S {
@@ -221,28 +199,15 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   auto* str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
-          create<ast::StructMember>(
-              Source{}, mod.RegisterSymbol("a"), "a", &i32,
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 4)}),
-          create<ast::StructMember>(
-              Source{}, mod.RegisterSymbol("b"), "b", &f32,
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 32)}),
-          create<ast::StructMember>(
-              Source{}, mod.RegisterSymbol("c"), "c", &f32,
-              ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 128)}),
+          Member("a", ty.i32, {MemberOffset(4)}),
+          Member("b", ty.f32, {MemberOffset(32)}),
+          Member("c", ty.f32, {MemberOffset(128)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   ASSERT_TRUE(gen.EmitStructType(&s)) << gen.error();
   EXPECT_EQ(gen.result(), R"(struct S {
@@ -257,22 +222,11 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Struct_NameCollision) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("main", ty.i32), Member("float", ty.f32)},
+      ast::StructDecorationList{});
 
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("main"), "main",
-                                &i32, ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod.RegisterSymbol("float"), "float", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   ASSERT_TRUE(gen.EmitStructType(&s)) << gen.error();
   EXPECT_EQ(gen.result(), R"(struct S {
@@ -284,24 +238,14 @@
 
 // TODO(dsinclair): How to translate [[block]]
 TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Struct_WithDecoration) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"), "a", &i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>(Source{}));
-  auto* str = create<ast::Struct>(Source{}, members, decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      decos);
 
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   ASSERT_TRUE(gen.EmitType(&s, "")) << gen.error();
   EXPECT_EQ(gen.result(), R"(struct {
diff --git a/src/writer/msl/generator_impl_unary_op_test.cc b/src/writer/msl/generator_impl_unary_op_test.cc
index ad0bd07..1df4653 100644
--- a/src/writer/msl/generator_impl_unary_op_test.cc
+++ b/src/writer/msl/generator_impl_unary_op_test.cc
@@ -40,7 +40,7 @@
   auto params = GetParam();
 
   auto* expr = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("expr"), "expr");
+      Source{}, mod->RegisterSymbol("expr"), "expr");
   ast::UnaryOpExpression op(Source{}, params.op, expr);
 
   ASSERT_TRUE(gen.EmitExpression(&op)) << gen.error();
diff --git a/src/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
index a49113d..9758a34 100644
--- a/src/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -100,22 +100,12 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Struct) {
-  ast::type::F32 f32;
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(Source{}, mod.RegisterSymbol("a"), "a", &f32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, mod.RegisterSymbol("b"),
-                                              "b", &f32, b_deco));
-
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
-
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", str);
+  ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
   auto* var =
       create<ast::Variable>(Source{},                        // source
@@ -197,7 +187,7 @@
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_Private) {
   auto* ident = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("initializer"), "initializer");
+      Source{}, mod->RegisterSymbol("initializer"), "initializer");
 
   ast::type::F32 f32;
   auto* var =
diff --git a/src/writer/msl/test_helper.h b/src/writer/msl/test_helper.h
index 853b51a..554e628 100644
--- a/src/writer/msl/test_helper.h
+++ b/src/writer/msl/test_helper.h
@@ -19,6 +19,7 @@
 #include <utility>
 
 #include "gtest/gtest.h"
+#include "src/ast/builder.h"
 #include "src/ast/module.h"
 #include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
@@ -29,22 +30,11 @@
 
 /// Helper class for testing
 template <typename BASE>
-class TestHelperBase : public BASE {
+class TestHelperBase : public BASE, public ast::BuilderWithModule {
  public:
-  TestHelperBase() : td(&mod), gen(&mod) {}
+  TestHelperBase() : td(mod), gen(mod) {}
   ~TestHelperBase() = default;
 
-  /// Creates a new `ast::Node` owned by the Module. When the Module is
-  /// destructed, the `ast::Node` will also be destructed.
-  /// @param args the arguments to pass to the type constructor
-  /// @returns the node pointer
-  template <typename T, typename... ARGS>
-  T* create(ARGS&&... args) {
-    return mod.create<T>(std::forward<ARGS>(args)...);
-  }
-
-  /// The module
-  ast::Module mod;
   /// The type determiner
   TypeDeterminer td;
   /// The generator
diff --git a/src/writer/spirv/builder_accessor_expression_test.cc b/src/writer/spirv/builder_accessor_expression_test.cc
index 81ac445..e3de740 100644
--- a/src/writer/spirv/builder_accessor_expression_test.cc
+++ b/src/writer/spirv/builder_accessor_expression_test.cc
@@ -243,14 +243,10 @@
   // var ident : my_struct
   // ident.b
 
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32, decos));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, decos));
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32), Member("b", ty.f32)},
+      ast::StructDecorationList{});
 
-  auto* s = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   auto* var = Var("ident", ast::StorageClass::kFunction, &s_type);
@@ -291,24 +287,16 @@
   //
   // var ident : my_struct
   // ident.inner.a
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList inner_members;
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32, decos));
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, decos));
-
   ast::type::Struct inner_struct(
       mod->RegisterSymbol("Inner"), "Inner",
-      create<ast::Struct>(inner_members, ast::StructDecorationList{}));
-
-  ast::StructMemberList outer_members;
-  outer_members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("inner"), "inner", &inner_struct, decos));
+      create<ast::Struct>(
+          ast::StructMemberList{Member("a", ty.f32), Member("b", ty.f32)},
+          ast::StructDecorationList{}));
 
   ast::type::Struct s_type(
       mod->RegisterSymbol("my_struct"), "my_struct",
-      create<ast::Struct>(outer_members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("inner", &inner_struct)},
+                          ast::StructDecorationList{}));
 
   auto* var = Var("ident", ast::StorageClass::kFunction, &s_type);
 
@@ -350,26 +338,18 @@
   //
   // var ident : my_struct
   // ident.inner.a
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList inner_members;
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32, decos));
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, decos));
-
   ast::type::Struct inner_struct(
       mod->RegisterSymbol("Inner"), "Inner",
-      create<ast::Struct>(inner_members, ast::StructDecorationList{}));
+      create<ast::Struct>(
+          ast::StructMemberList{Member("a", ty.f32), Member("b", ty.f32)},
+          ast::StructDecorationList{}));
 
   ast::type::Alias alias(mod->RegisterSymbol("Inner"), "Inner", &inner_struct);
 
-  ast::StructMemberList outer_members;
-  outer_members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("inner"), "inner", &alias, decos));
-
   ast::type::Struct s_type(
       mod->RegisterSymbol("Outer"), "Outer",
-      create<ast::Struct>(outer_members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("inner", &alias)},
+                          ast::StructDecorationList{}));
 
   auto* var = Var("ident", ast::StorageClass::kFunction, &s_type);
 
@@ -410,25 +390,16 @@
   //
   // var ident : my_struct
   // ident.inner.a = 2.0f;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList inner_members;
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32, decos));
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, decos));
-
   ast::type::Struct inner_struct(
       mod->RegisterSymbol("Inner"), "Inner",
-      create<ast::Struct>(inner_members, ast::StructDecorationList{}));
-
-  ast::StructMemberList outer_members;
-  outer_members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("inner"), "inner", &inner_struct, decos));
+      create<ast::Struct>(
+          ast::StructMemberList{Member("a", ty.f32), Member("b", ty.f32)},
+          ast::StructDecorationList{}));
 
   ast::type::Struct s_type(
       mod->RegisterSymbol("my_struct"), "my_struct",
-      create<ast::Struct>(outer_members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("inner", &inner_struct)},
+                          ast::StructDecorationList{}));
 
   auto* var = Var("ident", ast::StorageClass::kFunction, &s_type);
 
@@ -476,24 +447,16 @@
   // var ident : my_struct
   // var store : f32 = ident.inner.a
 
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList inner_members;
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32, decos));
-  inner_members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, decos));
-
   ast::type::Struct inner_struct(
       mod->RegisterSymbol("Inner"), "Inner",
-      create<ast::Struct>(inner_members, ast::StructDecorationList{}));
-
-  ast::StructMemberList outer_members;
-  outer_members.push_back(create<ast::StructMember>(
-      mod->RegisterSymbol("inner"), "inner", &inner_struct, decos));
+      create<ast::Struct>(
+          ast::StructMemberList{Member("a", ty.f32), Member("b", ty.f32)},
+          ast::StructDecorationList{}));
 
   ast::type::Struct s_type(
       mod->RegisterSymbol("my_struct"), "my_struct",
-      create<ast::Struct>(outer_members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("inner", &inner_struct)},
+                          ast::StructDecorationList{}));
 
   auto* var = Var("ident", ast::StorageClass::kFunction, &s_type);
   auto* store = Var("store", ast::StorageClass::kFunction, ty.f32);
@@ -701,26 +664,19 @@
   // var index : array<A, 2>
   // index[0].foo[2].bar.baz.yx
 
-  ast::StructMemberDecorationList decos;
-
-  auto* s = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("baz"), "baz", ty.vec3<f32>(), decos)},
-      ast::StructDecorationList{});
+  auto* s =
+      create<ast::Struct>(ast::StructMemberList{Member("baz", ty.vec3<f32>())},
+                          ast::StructDecorationList{});
   ast::type::Struct c_type(mod->RegisterSymbol("C"), "C", s);
 
-  s = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("bar"), "bar", &c_type, decos)},
-      ast::StructDecorationList{});
+  s = create<ast::Struct>(ast::StructMemberList{Member("bar", &c_type)},
+                          ast::StructDecorationList{});
   ast::type::Struct b_type(mod->RegisterSymbol("B"), "B", s);
 
   ast::type::Array b_ary_type(&b_type, 3, ast::ArrayDecorationList{});
 
-  s = create<ast::Struct>(
-      ast::StructMemberList{create<ast::StructMember>(
-          mod->RegisterSymbol("foo"), "foo", &b_ary_type, decos)},
-      ast::StructDecorationList{});
+  s = create<ast::Struct>(ast::StructMemberList{Member("foo", &b_ary_type)},
+                          ast::StructDecorationList{});
   ast::type::Struct a_type(mod->RegisterSymbol("A"), "A", s);
 
   ast::type::Array a_ary_type(&a_type, 2, ast::ArrayDecorationList{});
diff --git a/src/writer/spirv/builder_assign_test.cc b/src/writer/spirv/builder_assign_test.cc
index ba5e5f5..c59eb4b 100644
--- a/src/writer/spirv/builder_assign_test.cc
+++ b/src/writer/spirv/builder_assign_test.cc
@@ -247,8 +247,6 @@
 }
 
 TEST_F(BuilderTest, Assign_StructMember) {
-  ast::type::F32 f32;
-
   // my_struct {
   //   a : f32
   //   b : f32
@@ -256,14 +254,10 @@
   // var ident : my_struct
   // ident.b = 4.0;
 
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &f32, decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("b"), "b", &f32, decos));
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32), Member("b", ty.f32)},
+      ast::StructDecorationList{});
 
-  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   ast::Variable v(Source{}, "ident", ast::StorageClass::kFunction, &s_type,
@@ -277,7 +271,7 @@
                                         "b"));
 
   auto* val = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 4.0f));
+      Source{}, create<ast::FloatLiteral>(Source{}, ty.f32, 4.0f));
 
   ast::AssignmentStatement assign(Source{}, ident, val);
 
diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc
index ed54fa1..da71c56 100644
--- a/src/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/writer/spirv/builder_constructor_expression_test.cc
@@ -953,14 +953,10 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Struct) {
-  ast::StructMemberDecorationList decos;
   auto* s = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("a"), "a",
-                                    ty.f32, decos),
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("b"), "b",
-                                    ty.vec3<f32>(), decos),
+          Member("a", ty.f32),
+          Member("b", ty.vec3<f32>()),
       },
       ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
@@ -1095,12 +1091,9 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_ZeroInit_Struct) {
-  ast::StructMemberDecorationList decos;
   auto* s = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("a"), "a",
-                                    ty.f32, decos),
+          Member("a", ty.f32),
       },
       ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
@@ -1511,14 +1504,10 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Struct) {
-  ast::StructMemberDecorationList decos;
   auto* s = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("a"), "a",
-                                    ty.f32, decos),
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("b"), "b",
-                                    ty.vec3<f32>(), decos),
+          Member("a", ty.f32),
+          Member("b", ty.vec3<f32>()),
       },
       ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
@@ -1533,14 +1522,10 @@
 
 TEST_F(SpvBuilderConstructorTest,
        IsConstructorConst_Struct_WithIdentSubExpression) {
-  ast::StructMemberDecorationList decos;
   auto* s = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("a"), "a",
-                                    ty.f32, decos),
-          create<ast::StructMember>(Source{}, mod->RegisterSymbol("b"), "b",
-                                    ty.vec3<f32>(), decos),
+          Member("a", ty.f32),
+          Member("b", ty.vec3<f32>()),
       },
       ast::StructDecorationList{});
 
diff --git a/src/writer/spirv/builder_function_test.cc b/src/writer/spirv/builder_function_test.cc
index a5230d3..adc847a 100644
--- a/src/writer/spirv/builder_function_test.cc
+++ b/src/writer/spirv/builder_function_test.cc
@@ -270,18 +270,12 @@
   // }
 
   ast::type::Void void_type;
-  ast::type::F32 f32;
-
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("d"), "d", &f32, a_deco));
 
   ast::StructDecorationList s_decos;
   s_decos.push_back(create<ast::StructBlockDecoration>(Source{}));
 
-  auto* str = create<ast::Struct>(Source{}, members, s_decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("d", ty.f32, {MemberOffset(0)})}, s_decos);
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
@@ -310,7 +304,7 @@
         Source{},                      // source
         "v",                           // name
         ast::StorageClass::kFunction,  // storage_class
-        &f32,                          // type
+        ty.f32,                        // type
         false,                         // is_const
         create<ast::MemberAccessorExpression>(
             Source{},
@@ -342,7 +336,7 @@
         Source{},                      // source
         "v",                           // name
         ast::StorageClass::kFunction,  // storage_class
-        &f32,                          // type
+        ty.f32,                        // type
         false,                         // is_const
         create<ast::MemberAccessorExpression>(
             Source{},
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index 3ebce67..cd5537c 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -526,18 +526,11 @@
   // };
   // var b : [[access(read)]] A
 
-  ast::type::I32 i32;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &i32, decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("b"), "b", &i32, decos));
-
   ast::type::Struct A(
       mod->RegisterSymbol("A"), "A",
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{}));
+      create<ast::Struct>(
+          ast::StructMemberList{Member("a", ty.i32), Member("b", ty.i32)},
+          ast::StructDecorationList{}));
   ast::type::AccessControl ac{ast::AccessControl::kReadOnly, &A};
 
   ast::Variable var(Source{}, "b", ast::StorageClass::kStorageBuffer, &ac,
@@ -567,16 +560,10 @@
   // type B = A;
   // var b : [[access(read)]] B
 
-  ast::type::I32 i32;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &i32, decos));
-
   ast::type::Struct A(
       mod->RegisterSymbol("A"), "A",
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32)},
+                          ast::StructDecorationList{}));
   ast::type::Alias B(mod->RegisterSymbol("B"), "B", &A);
   ast::type::AccessControl ac{ast::AccessControl::kReadOnly, &B};
 
@@ -605,16 +592,10 @@
   // type B = [[access(read)]] A;
   // var b : B
 
-  ast::type::I32 i32;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &i32, decos));
-
   ast::type::Struct A(
       mod->RegisterSymbol("A"), "A",
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32)},
+                          ast::StructDecorationList{}));
   ast::type::AccessControl ac{ast::AccessControl::kReadOnly, &A};
   ast::type::Alias B(mod->RegisterSymbol("B"), "B", &ac);
 
@@ -643,16 +624,10 @@
   // var b : [[access(read)]] A
   // var c : [[access(read_write)]] A
 
-  ast::type::I32 i32;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &i32, decos));
-
   ast::type::Struct A(
       mod->RegisterSymbol("A"), "A",
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{}));
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32)},
+                          ast::StructDecorationList{}));
   ast::type::AccessControl read{ast::AccessControl::kReadOnly, &A};
   ast::type::AccessControl rw{ast::AccessControl::kReadWrite, &A};
 
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 7ac5fa5..97b8028 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -1332,12 +1332,9 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_ArrayLength) {
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.array<f32>(), decos));
-
-  auto* s = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* s =
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.array<f32>())},
+                          ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   auto* var = Var("b", ast::StorageClass::kPrivate, &s_type);
@@ -1374,14 +1371,10 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) {
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("z"), "z", ty.f32, decos));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.array<f32>(), decos));
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("z", ty.f32), Member("a", ty.array<f32>())},
+      ast::StructDecorationList{});
 
-  auto* s = create<ast::Struct>(members, ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   auto* var = Var("b", ast::StorageClass::kPrivate, &s_type);
@@ -1420,14 +1413,11 @@
 TEST_F(IntrinsicBuilderTest, DISABLED_Call_ArrayLength_Ptr) {
   ast::type::Pointer ptr(ty.array<f32>(), ast::StorageClass::kStorageBuffer);
 
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("z"), "z", ty.f32, decos));
-  members.push_back(create<ast::StructMember>(mod->RegisterSymbol("a"), "a",
-                                              ty.array<f32>(), decos));
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("z", ty.f32), Member("a", ty.array<f32>())
 
-  auto* s = create<ast::Struct>(members, ast::StructDecorationList{});
+      },
+      ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   auto* var = Var("b", ast::StorageClass::kPrivate, &s_type);
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index 7287f17..34d185c 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -294,14 +294,8 @@
 }
 
 TEST_F(BuilderTest_Type, GenerateStruct) {
-  ast::type::F32 f32;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &f32, decos));
-
-  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s = create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32)},
+                                ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   auto id = b.GenerateTypeIfNeeded(&s_type);
@@ -317,17 +311,11 @@
 }
 
 TEST_F(BuilderTest_Type, GenerateStruct_Decorated) {
-  ast::type::F32 f32;
-
-  ast::StructMemberDecorationList decos;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &f32, decos));
-
   ast::StructDecorationList struct_decos;
   struct_decos.push_back(create<ast::StructBlockDecoration>(Source{}));
 
-  auto* s = create<ast::Struct>(Source{}, members, struct_decos);
+  auto* s = create<ast::Struct>(ast::StructMemberList{Member("a", ty.f32)},
+                                struct_decos);
   ast::type::Struct s_type(mod->RegisterSymbol("my_struct"), "my_struct", s);
 
   auto id = b.GenerateTypeIfNeeded(&s_type);
@@ -345,20 +333,10 @@
 }
 
 TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) {
-  ast::type::F32 f32;
-
-  ast::StructMemberDecorationList a_decos;
-  a_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  ast::StructMemberDecorationList b_decos;
-  b_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 8));
-
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &f32, a_decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("b"), "b", &f32, b_decos));
-
-  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32, {MemberOffset(0)}),
+                            Member("b", ty.f32, {MemberOffset(8)})},
+      ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("S"), "S", s);
 
   auto id = b.GenerateTypeIfNeeded(&s_type);
@@ -378,24 +356,11 @@
 }
 
 TEST_F(BuilderTest_Type, GenerateStruct_NonLayout_Matrix) {
-  // Don't infer layout for matrix when there is no offset.
-  ast::type::F32 f32;
-  ast::type::Matrix glsl_mat2x2(&f32, 2, 2);
-  ast::type::Matrix glsl_mat2x3(&f32, 3, 2);  // 2 columns, 3 rows
-  ast::type::Matrix glsl_mat4x4(&f32, 4, 4);
-
-  ast::StructMemberDecorationList empty_a;
-  ast::StructMemberDecorationList empty_b;
-  ast::StructMemberDecorationList empty_c;
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &glsl_mat2x2, empty_a));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("b"), "b", &glsl_mat2x3, empty_b));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("c"), "c", &glsl_mat4x4, empty_c));
-
-  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s =
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.mat2x2<f32>()),
+                                                Member("b", ty.mat2x3<f32>()),
+                                                Member("c", ty.mat4x4<f32>())},
+                          ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("S"), "S", s);
 
   auto id = b.GenerateTypeIfNeeded(&s_type);
@@ -421,27 +386,11 @@
 
 TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutMatrix) {
   // We have to infer layout for matrix when it also has an offset.
-  ast::type::F32 f32;
-  ast::type::Matrix glsl_mat2x2(&f32, 2, 2);
-  ast::type::Matrix glsl_mat2x3(&f32, 3, 2);  // 2 columns, 3 rows
-  ast::type::Matrix glsl_mat4x4(&f32, 4, 4);
-
-  ast::StructMemberDecorationList a_decos;
-  a_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  ast::StructMemberDecorationList b_decos;
-  b_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 16));
-  ast::StructMemberDecorationList c_decos;
-  c_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 48));
-
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &glsl_mat2x2, a_decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("b"), "b", &glsl_mat2x3, b_decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("c"), "c", &glsl_mat4x4, c_decos));
-
-  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.mat2x2<f32>(), {MemberOffset(0)}),
+                            Member("b", ty.mat2x3<f32>(), {MemberOffset(16)}),
+                            Member("c", ty.mat4x4<f32>(), {MemberOffset(48)})},
+      ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("S"), "S", s);
 
   auto id = b.GenerateTypeIfNeeded(&s_type);
@@ -478,51 +427,40 @@
   // We have to infer layout for matrix when it also has an offset.
   // The decoration goes on the struct member, even if the matrix is buried
   // in levels of arrays.
-  ast::type::F32 f32;
-
-  ast::type::Matrix glsl_mat2x2(&f32, 2, 2);
   ast::type::Array arr_mat2x2(
-      &glsl_mat2x2, 1, ast::ArrayDecorationList{});  // Singly nested array
+      ty.mat2x2<f32>(), 1, ast::ArrayDecorationList{});  // Singly nested array
 
-  ast::type::Matrix glsl_mat2x3(&f32, 3, 2);  // 2 columns, 3 rows
-  ast::type::Array arr_mat2x3(&glsl_mat2x3, 1, ast::ArrayDecorationList{});
-  ast::type::Array arr_arr_mat2x2(
-      &arr_mat2x3, 1, ast::ArrayDecorationList{});  // Doubly nested array
+  ast::type::Array arr_mat2x3(ty.mat2x3<f32>(), 1, ast::ArrayDecorationList{});
+  ast::type::Array arr_arr_mat2x3(
+      ty.mat2x3<f32>(), 1, ast::ArrayDecorationList{});  // Doubly nested array
 
-  ast::type::Matrix glsl_mat4x4(&f32, 4, 4);
-  ast::type::Array rtarr_mat4x4(&glsl_mat4x4, 0,
+  ast::type::Array rtarr_mat4x4(ty.mat4x4<f32>(), 0,
                                 ast::ArrayDecorationList{});  // Runtime array
 
-  ast::StructMemberDecorationList a_decos;
-  a_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  ast::StructMemberDecorationList b_decos;
-  b_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 16));
-  ast::StructMemberDecorationList c_decos;
-  c_decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 48));
-
-  ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("a"), "a", &glsl_mat2x2, a_decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("b"), "b", &glsl_mat2x3, b_decos));
-  members.push_back(create<ast::StructMember>(
-      Source{}, mod->RegisterSymbol("c"), "c", &glsl_mat4x4, c_decos));
-
-  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s = create<ast::Struct>(
+      ast::StructMemberList{Member("a", &arr_mat2x2, {MemberOffset(0)}),
+                            Member("b", &arr_arr_mat2x3, {MemberOffset(16)}),
+                            Member("c", &rtarr_mat4x4, {MemberOffset(48)})},
+      ast::StructDecorationList{});
   ast::type::Struct s_type(mod->RegisterSymbol("S"), "S", s);
 
   auto id = b.GenerateTypeIfNeeded(&s_type);
   ASSERT_FALSE(b.has_error()) << b.error();
   EXPECT_EQ(id, 1u);
 
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 2
-%2 = OpTypeMatrix %3 2
-%6 = OpTypeVector %4 3
-%5 = OpTypeMatrix %6 2
-%8 = OpTypeVector %4 4
-%7 = OpTypeMatrix %8 4
-%1 = OpTypeStruct %2 %5 %7
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
+%4 = OpTypeVector %5 2
+%3 = OpTypeMatrix %4 2
+%6 = OpTypeInt 32 0
+%7 = OpConstant %6 1
+%2 = OpTypeArray %3 %7
+%10 = OpTypeVector %5 3
+%9 = OpTypeMatrix %10 2
+%8 = OpTypeArray %9 %7
+%13 = OpTypeVector %5 4
+%12 = OpTypeMatrix %13 4
+%11 = OpTypeRuntimeArray %12
+%1 = OpTypeStruct %2 %8 %11
 )");
   EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "S"
 OpMemberName %1 0 "a"
diff --git a/src/writer/wgsl/generator_impl_alias_type_test.cc b/src/writer/wgsl/generator_impl_alias_type_test.cc
index e7bdab0..5e654ea 100644
--- a/src/writer/wgsl/generator_impl_alias_type_test.cc
+++ b/src/writer/wgsl/generator_impl_alias_type_test.cc
@@ -16,9 +16,6 @@
 #include "src/ast/struct.h"
 #include "src/ast/struct_member.h"
 #include "src/ast/struct_member_decoration.h"
-#include "src/ast/struct_member_offset_decoration.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/writer/wgsl/generator_impl.h"
 #include "src/writer/wgsl/test_helper.h"
@@ -39,17 +36,10 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructedType_Struct) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.i32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32),
+                            Member("b", ty.i32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("A"), "A", str);
   ast::type::Alias alias(mod->RegisterSymbol("B"), "B", &s);
@@ -66,17 +56,10 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitAlias_ToStruct) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.f32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.i32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.f32),
+                            Member("b", ty.i32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("A"), "A", str);
   ast::type::Alias alias(mod->RegisterSymbol("B"), "B", &s);
diff --git a/src/writer/wgsl/generator_impl_function_test.cc b/src/writer/wgsl/generator_impl_function_test.cc
index 3a295b6..2b224c3 100644
--- a/src/writer/wgsl/generator_impl_function_test.cc
+++ b/src/writer/wgsl/generator_impl_function_test.cc
@@ -176,16 +176,11 @@
   //   return;
   // }
 
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("d"), "d", ty.f32, a_deco));
-
   ast::StructDecorationList s_decos;
   s_decos.push_back(create<ast::StructBlockDecoration>());
 
-  auto* str = create<ast::Struct>(members, s_decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("d", ty.f32, {MemberOffset(0)})}, s_decos);
 
   ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc
index 5f33cf3..6faf4a9 100644
--- a/src/writer/wgsl/generator_impl_type_test.cc
+++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -60,16 +60,12 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_Read) {
-  auto* mem = create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                        ast::StructMemberDecorationList{});
-  ast::StructMemberList members;
-  members.push_back(mem);
-
   auto* block_deco = create<ast::StructBlockDecoration>();
   ast::StructDecorationList decos;
   decos.push_back(block_deco);
 
-  auto* str = create<ast::Struct>(members, decos);
+  auto* str =
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32)}, decos);
   auto* s = create<ast::type::Struct>(mod->RegisterSymbol("S"), "S", str);
 
   ast::type::AccessControl a(ast::AccessControl::kReadOnly, s);
@@ -79,16 +75,12 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_ReadWrite) {
-  auto* mem = create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                        ast::StructMemberDecorationList{});
-  ast::StructMemberList members;
-  members.push_back(mem);
-
   auto* block_deco = create<ast::StructBlockDecoration>();
   ast::StructDecorationList decos;
   decos.push_back(block_deco);
 
-  auto* str = create<ast::Struct>(members, decos);
+  auto* str =
+      create<ast::Struct>(ast::StructMemberList{Member("a", ty.i32)}, decos);
   auto* s = create<ast::type::Struct>(mod->RegisterSymbol("S"), "S", str);
 
   ast::type::AccessControl a(ast::AccessControl::kReadWrite, s);
@@ -153,17 +145,10 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Struct) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
@@ -172,17 +157,10 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_StructDecl) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
-  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      ast::StructDecorationList{});
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);
 
@@ -196,20 +174,13 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) {
-  ast::StructMemberList members;
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("a"), "a", ty.i32,
-                                ast::StructMemberDecorationList{}));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      create<ast::StructMember>(mod->RegisterSymbol("b"), "b", ty.f32, b_deco));
-
   ast::StructDecorationList decos;
   decos.push_back(create<ast::StructBlockDecoration>());
 
-  auto* str = create<ast::Struct>(members, decos);
+  auto* str = create<ast::Struct>(
+      ast::StructMemberList{Member("a", ty.i32),
+                            Member("b", ty.f32, {MemberOffset(4)})},
+      decos);
 
   ast::type::Struct s(mod->RegisterSymbol("S"), "S", str);