Start cleaning up tests (2/N)

Remove Source{} with ast::Builder::create<>
Use Builder helpers where possible

Change-Id: Ia29fe615ac8975252a34f1252c363109ee382d72
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/35508
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/ast/builder.h b/src/ast/builder.h
index 4a4b26a..254bd1f 100644
--- a/src/ast/builder.h
+++ b/src/ast/builder.h
@@ -144,7 +144,7 @@
   }
 
   /// @param subtype the array element type
-  /// @param n the array size. 0 represents unbounded
+  /// @param n the array size. 0 represents a runtime-array.
   /// @return the tint AST type for a array of size `n` of type `T`
   type::Array* array(type::Type* subtype, uint32_t n) const {
     return mod_->create<type::Array>(subtype, n, ArrayDecorationList{});
@@ -422,7 +422,7 @@
   }
 
   /// @param args the arguments for the array constructor
-  /// @return an `TypeConstructorExpression` of a 4x4 matrix of type
+  /// @return an `TypeConstructorExpression` of an array with element type
   /// `T`, constructed with the values `args`.
   template <typename T, int N = 0, typename... ARGS>
   TypeConstructorExpression* array(ARGS&&... args) {
@@ -430,6 +430,19 @@
         Source{}, ty.array<T, N>(), ExprList(std::forward<ARGS>(args)...));
   }
 
+  /// @param subtype the array element type
+  /// @param n the array size. 0 represents a runtime-array.
+  /// @param args the arguments for the array constructor
+  /// @return an `TypeConstructorExpression` of an array with element type
+  /// `subtype`, constructed with the values `args`.
+  template <typename... ARGS>
+  TypeConstructorExpression* array(type::Type* subtype,
+                                   uint32_t n,
+                                   ARGS&&... args) {
+    return create<TypeConstructorExpression>(
+        Source{}, ty.array(subtype, n), ExprList(std::forward<ARGS>(args)...));
+  }
+
   /// @param name the variable name
   /// @param storage the variable storage class
   /// @param type the variable type
diff --git a/src/writer/wgsl/generator_impl_alias_type_test.cc b/src/writer/wgsl/generator_impl_alias_type_test.cc
index 1bbc96b..a0b3a3e 100644
--- a/src/writer/wgsl/generator_impl_alias_type_test.cc
+++ b/src/writer/wgsl/generator_impl_alias_type_test.cc
@@ -31,8 +31,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitAlias_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"(type a = f32;
@@ -40,22 +39,18 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructedType_Struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "a", &f32, ast::StructMemberDecorationList{}));
+      "a", ty.f32, ast::StructMemberDecorationList{}));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &i32, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("b", ty.i32, b_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("A"), "A", str);
-  ast::type::Alias alias(mod.RegisterSymbol("B"), "B", &s);
+  ast::type::Struct s(mod->RegisterSymbol("A"), "A", str);
+  ast::type::Alias alias(mod->RegisterSymbol("B"), "B", &s);
 
   ASSERT_TRUE(gen.EmitConstructedType(&s)) << gen.error();
   ASSERT_TRUE(gen.EmitConstructedType(&alias)) << gen.error();
@@ -69,22 +64,18 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitAlias_ToStruct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "a", &f32, ast::StructMemberDecorationList{}));
+      "a", ty.f32, ast::StructMemberDecorationList{}));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &i32, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("b", ty.i32, b_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("A"), "A", str);
-  ast::type::Alias alias(mod.RegisterSymbol("B"), "B", &s);
+  ast::type::Struct s(mod->RegisterSymbol("A"), "A", str);
+  ast::type::Alias alias(mod->RegisterSymbol("B"), "B", &s);
 
   ASSERT_TRUE(gen.EmitConstructedType(&alias)) << gen.error();
   EXPECT_EQ(gen.result(), R"(type B = A;
diff --git a/src/writer/wgsl/generator_impl_array_accessor_test.cc b/src/writer/wgsl/generator_impl_array_accessor_test.cc
index 551600d..289761d 100644
--- a/src/writer/wgsl/generator_impl_array_accessor_test.cc
+++ b/src/writer/wgsl/generator_impl_array_accessor_test.cc
@@ -31,27 +31,23 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_ArrayAccessor) {
-  ast::type::I32 i32;
-  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");
+  auto* lit = create<ast::SintLiteral>(ty.i32, 5);
+  auto* idx = create<ast::ScalarConstructorExpression>(lit);
+  auto* ary = Expr("ary");
 
-  ast::ArrayAccessorExpression expr(Source{}, ary, idx);
+  auto* expr = create<ast::ArrayAccessorExpression>(ary, idx);
 
-  ASSERT_TRUE(gen.EmitExpression(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(expr)) << gen.error();
   EXPECT_EQ(gen.result(), "ary[5]");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitArrayAccessor) {
-  auto* ary = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("ary"), "ary");
-  auto* idx = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("idx"), "idx");
+  auto* ary = Expr("ary");
+  auto* idx = Expr("idx");
 
-  ast::ArrayAccessorExpression expr(Source{}, ary, idx);
+  auto* expr = create<ast::ArrayAccessorExpression>(ary, idx);
 
-  ASSERT_TRUE(gen.EmitArrayAccessor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitArrayAccessor(expr)) << gen.error();
   EXPECT_EQ(gen.result(), "ary[idx]");
 }
 
diff --git a/src/writer/wgsl/generator_impl_assign_test.cc b/src/writer/wgsl/generator_impl_assign_test.cc
index 0d3cde4..fc720a0 100644
--- a/src/writer/wgsl/generator_impl_assign_test.cc
+++ b/src/writer/wgsl/generator_impl_assign_test.cc
@@ -29,15 +29,13 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Assign) {
-  auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("lhs"), "lhs");
-  auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("rhs"), "rhs");
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&assign)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(assign)) << gen.error();
   EXPECT_EQ(gen.result(), "  lhs = rhs;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_binary_test.cc b/src/writer/wgsl/generator_impl_binary_test.cc
index 2c55ab6..70f3fa7 100644
--- a/src/writer/wgsl/generator_impl_binary_test.cc
+++ b/src/writer/wgsl/generator_impl_binary_test.cc
@@ -37,14 +37,12 @@
 TEST_P(WgslBinaryTest, Emit) {
   auto params = GetParam();
 
-  auto* left = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("left"), "left");
-  auto* right = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("right"), "right");
+  auto* left = Expr("left");
+  auto* right = Expr("right");
 
-  ast::BinaryExpression expr(Source{}, params.op, left, right);
+  auto* expr = create<ast::BinaryExpression>(params.op, left, right);
 
-  ASSERT_TRUE(gen.EmitExpression(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(expr)) << gen.error();
   EXPECT_EQ(gen.result(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
diff --git a/src/writer/wgsl/generator_impl_bitcast_test.cc b/src/writer/wgsl/generator_impl_bitcast_test.cc
index 26f7257..01c6d5e 100644
--- a/src/writer/wgsl/generator_impl_bitcast_test.cc
+++ b/src/writer/wgsl/generator_impl_bitcast_test.cc
@@ -29,12 +29,10 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_Bitcast) {
-  ast::type::F32 f32;
-  auto* id = create<ast::IdentifierExpression>(Source{},
-                                               mod.RegisterSymbol("id"), "id");
-  ast::BitcastExpression bitcast(Source{}, &f32, id);
+  auto* id = Expr("id");
+  auto* bitcast = create<ast::BitcastExpression>(ty.f32, id);
 
-  ASSERT_TRUE(gen.EmitExpression(&bitcast)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(bitcast)) << gen.error();
   EXPECT_EQ(gen.result(), "bitcast<f32>(id)");
 }
 
diff --git a/src/writer/wgsl/generator_impl_block_test.cc b/src/writer/wgsl/generator_impl_block_test.cc
index ad15b5d..ef843d4 100644
--- a/src/writer/wgsl/generator_impl_block_test.cc
+++ b/src/writer/wgsl/generator_impl_block_test.cc
@@ -28,12 +28,12 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Block) {
-  ast::BlockStatement b(Source{}, ast::StatementList{
-                                      create<ast::DiscardStatement>(Source{}),
-                                  });
+  auto* b = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&b)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  {
     discard;
   }
@@ -41,12 +41,12 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Block_WithoutNewline) {
-  ast::BlockStatement b(Source{}, ast::StatementList{
-                                      create<ast::DiscardStatement>(Source{}),
-                                  });
+  auto* b = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitBlock(&b)) << gen.error();
+  ASSERT_TRUE(gen.EmitBlock(b)) << gen.error();
   EXPECT_EQ(gen.result(), R"({
     discard;
   })");
diff --git a/src/writer/wgsl/generator_impl_break_test.cc b/src/writer/wgsl/generator_impl_break_test.cc
index 361f6c0..bb0385a 100644
--- a/src/writer/wgsl/generator_impl_break_test.cc
+++ b/src/writer/wgsl/generator_impl_break_test.cc
@@ -28,11 +28,11 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Break) {
-  ast::BreakStatement b(Source{});
+  auto* b = create<ast::BreakStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&b)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(b)) << gen.error();
   EXPECT_EQ(gen.result(), "  break;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_call_test.cc b/src/writer/wgsl/generator_impl_call_test.cc
index 6ad5e38..f51e3da 100644
--- a/src/writer/wgsl/generator_impl_call_test.cc
+++ b/src/writer/wgsl/generator_impl_call_test.cc
@@ -29,42 +29,30 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_Call_WithoutParams) {
-  auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
-  ast::CallExpression call(Source{}, id, {});
+  auto* id = Expr("my_func");
+  auto* call = create<ast::CallExpression>(id, ast::ExpressionList{});
 
-  ASSERT_TRUE(gen.EmitExpression(&call)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(call)) << gen.error();
   EXPECT_EQ(gen.result(), "my_func()");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_Call_WithParams) {
-  auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param1"), "param1"));
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param2"), "param2"));
-  ast::CallExpression call(Source{}, id, params);
+  auto* id = Expr("my_func");
+  auto* call = create<ast::CallExpression>(
+      id, ast::ExpressionList{Expr("param1"), Expr("param2")});
 
-  ASSERT_TRUE(gen.EmitExpression(&call)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(call)) << gen.error();
   EXPECT_EQ(gen.result(), "my_func(param1, param2)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitStatement_Call) {
-  auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param1"), "param1"));
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("param2"), "param2"));
+  auto* id = Expr("my_func");
 
-  ast::CallStatement call(Source{},
-                          create<ast::CallExpression>(Source{}, id, params));
+  auto* call = create<ast::CallStatement>(create<ast::CallExpression>(
+      id, ast::ExpressionList{Expr("param1"), Expr("param2")}));
 
   gen.increment_indent();
-  ASSERT_TRUE(gen.EmitStatement(&call)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(call)) << gen.error();
   EXPECT_EQ(gen.result(), "  my_func(param1, param2);\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_case_test.cc b/src/writer/wgsl/generator_impl_case_test.cc
index aa31215..ff2f393 100644
--- a/src/writer/wgsl/generator_impl_case_test.cc
+++ b/src/writer/wgsl/generator_impl_case_test.cc
@@ -31,19 +31,16 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Case) {
-  ast::type::I32 i32;
-
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::BreakStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::BreakStatement>(),
+  });
   ast::CaseSelectorList lit;
-  lit.push_back(create<ast::SintLiteral>(Source{}, &i32, 5));
-  ast::CaseStatement c(Source{}, lit, body);
+  lit.push_back(create<ast::SintLiteral>(ty.i32, 5));
+  auto* c = create<ast::CaseStatement>(lit, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(&c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(c)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  case 5: {
     break;
   }
@@ -51,20 +48,17 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Case_MultipleSelectors) {
-  ast::type::I32 i32;
-
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::BreakStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::BreakStatement>(),
+  });
   ast::CaseSelectorList lit;
-  lit.push_back(create<ast::SintLiteral>(Source{}, &i32, 5));
-  lit.push_back(create<ast::SintLiteral>(Source{}, &i32, 6));
-  ast::CaseStatement c(Source{}, lit, body);
+  lit.push_back(create<ast::SintLiteral>(ty.i32, 5));
+  lit.push_back(create<ast::SintLiteral>(ty.i32, 6));
+  auto* c = create<ast::CaseStatement>(lit, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(&c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(c)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  case 5, 6: {
     break;
   }
@@ -72,15 +66,14 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Case_Default) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::BreakStatement>(Source{}),
-                });
-  ast::CaseStatement c(Source{}, ast::CaseSelectorList{}, body);
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::BreakStatement>(),
+  });
+  auto* c = create<ast::CaseStatement>(ast::CaseSelectorList{}, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(&c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(c)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  default: {
     break;
   }
diff --git a/src/writer/wgsl/generator_impl_cast_test.cc b/src/writer/wgsl/generator_impl_cast_test.cc
index 97b5fc6..b90a7a1 100644
--- a/src/writer/wgsl/generator_impl_cast_test.cc
+++ b/src/writer/wgsl/generator_impl_cast_test.cc
@@ -29,15 +29,9 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_Cast) {
-  ast::type::F32 f32;
+  auto* cast = Construct<f32>("id");
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("id"), "id"));
-
-  ast::TypeConstructorExpression cast(Source{}, &f32, params);
-
-  ASSERT_TRUE(gen.EmitExpression(&cast)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(cast)) << gen.error();
   EXPECT_EQ(gen.result(), "f32(id)");
 }
 
diff --git a/src/writer/wgsl/generator_impl_constructor_test.cc b/src/writer/wgsl/generator_impl_constructor_test.cc
index cc6a30d..31a577e 100644
--- a/src/writer/wgsl/generator_impl_constructor_test.cc
+++ b/src/writer/wgsl/generator_impl_constructor_test.cc
@@ -36,172 +36,71 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Bool) {
-  ast::type::Bool bool_type;
-  auto* lit = create<ast::BoolLiteral>(Source{}, &bool_type, false);
-  ast::ScalarConstructorExpression expr(Source{}, lit);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Expr(false))) << gen.error();
   EXPECT_EQ(gen.result(), "false");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Int) {
-  ast::type::I32 i32;
-  auto* lit = create<ast::SintLiteral>(Source{}, &i32, -12345);
-  ast::ScalarConstructorExpression expr(Source{}, lit);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Expr(-12345))) << gen.error();
   EXPECT_EQ(gen.result(), "-12345");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_UInt) {
-  ast::type::U32 u32;
-  auto* lit = create<ast::UintLiteral>(Source{}, &u32, 56779);
-  ast::ScalarConstructorExpression expr(Source{}, lit);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Expr(56779u))) << gen.error();
   EXPECT_EQ(gen.result(), "56779u");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Float) {
-  ast::type::F32 f32;
   // Use a number close to 1<<30 but whose decimal representation ends in 0.
-  auto* lit = create<ast::FloatLiteral>(Source{}, &f32,
-                                        static_cast<float>((1 << 30) - 4));
-  ast::ScalarConstructorExpression expr(Source{}, lit);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Expr(static_cast<float>((1 << 30) - 4))))
+      << gen.error();
   EXPECT_EQ(gen.result(), "1073741824.0");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Float) {
-  ast::type::F32 f32;
-
-  auto* lit = create<ast::FloatLiteral>(Source{}, &f32, -1.2e-5);
-  ast::ExpressionList values;
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit));
-
-  ast::TypeConstructorExpression expr(Source{}, &f32, values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Construct<f32>(Expr(-1.2e-5f))))
+      << gen.error();
   EXPECT_EQ(gen.result(), "f32(-0.000012)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Bool) {
-  ast::type::Bool b;
-
-  auto* lit = create<ast::BoolLiteral>(Source{}, &b, true);
-  ast::ExpressionList values;
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit));
-
-  ast::TypeConstructorExpression expr(Source{}, &b, values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Construct<bool>(true))) << gen.error();
   EXPECT_EQ(gen.result(), "bool(true)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Int) {
-  ast::type::I32 i32;
-
-  auto* lit = create<ast::SintLiteral>(Source{}, &i32, -12345);
-  ast::ExpressionList values;
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit));
-
-  ast::TypeConstructorExpression expr(Source{}, &i32, values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Construct<i32>(-12345))) << gen.error();
   EXPECT_EQ(gen.result(), "i32(-12345)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Uint) {
-  ast::type::U32 u32;
-
-  auto* lit = create<ast::UintLiteral>(Source{}, &u32, 12345);
-  ast::ExpressionList values;
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit));
-
-  ast::TypeConstructorExpression expr(Source{}, &u32, values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(Construct<u32>(12345u))) << gen.error();
   EXPECT_EQ(gen.result(), "u32(12345u)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Vec) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
-
-  auto* lit1 = create<ast::FloatLiteral>(Source{}, &f32, 1.f);
-  auto* lit2 = create<ast::FloatLiteral>(Source{}, &f32, 2.f);
-  auto* lit3 = create<ast::FloatLiteral>(Source{}, &f32, 3.f);
-  ast::ExpressionList values;
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit1));
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit2));
-  values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit3));
-
-  ast::TypeConstructorExpression expr(Source{}, &vec, values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(vec3<f32>(1.f, 2.f, 3.f))) << gen.error();
   EXPECT_EQ(gen.result(), "vec3<f32>(1.0, 2.0, 3.0)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Mat) {
-  ast::type::F32 f32;
-  ast::type::Matrix mat(&f32, 3, 2);
-
-  ast::type::Vector vec(&f32, 2);
-
-  ast::ExpressionList mat_values;
-
-  for (size_t i = 0; i < 3; i++) {
-    auto* lit1 = create<ast::FloatLiteral>(Source{}, &f32,
-                                           static_cast<float>(1 + (i * 2)));
-    auto* lit2 = create<ast::FloatLiteral>(Source{}, &f32,
-                                           static_cast<float>(2 + (i * 2)));
-
-    ast::ExpressionList values;
-    values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit1));
-    values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit2));
-
-    mat_values.push_back(
-        create<ast::TypeConstructorExpression>(Source{}, &vec, values));
-  }
-
-  ast::TypeConstructorExpression expr(Source{}, &mat, mat_values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), std::string("mat2x3<f32>(vec2<f32>(1.0, 2.0), ") +
-                              "vec2<f32>(3.0, 4.0), " + "vec2<f32>(5.0, 6.0))");
+  ASSERT_TRUE(gen.EmitConstructor(
+      Construct(ty.mat2x3<f32>(), vec2<f32>(1.0f, 2.0f), vec2<f32>(3.0f, 4.0f),
+                vec2<f32>(5.0f, 6.0f))))
+      << gen.error();
+  EXPECT_EQ(gen.result(),
+            "mat2x3<f32>(vec2<f32>(1.0, 2.0), vec2<f32>(3.0, 4.0), "
+            "vec2<f32>(5.0, 6.0))");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Array) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
-  ast::type::Array ary(&vec, 3, ast::ArrayDecorationList{});
-
-  ast::ExpressionList ary_values;
-
-  for (size_t i = 0; i < 3; i++) {
-    auto* lit1 = create<ast::FloatLiteral>(Source{}, &f32,
-                                           static_cast<float>(1 + (i * 3)));
-    auto* lit2 = create<ast::FloatLiteral>(Source{}, &f32,
-                                           static_cast<float>(2 + (i * 3)));
-    auto* lit3 = create<ast::FloatLiteral>(Source{}, &f32,
-                                           static_cast<float>(3 + (i * 3)));
-
-    ast::ExpressionList values;
-    values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit1));
-    values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit2));
-    values.push_back(create<ast::ScalarConstructorExpression>(Source{}, lit3));
-
-    ary_values.push_back(
-        create<ast::TypeConstructorExpression>(Source{}, &vec, values));
-  }
-
-  ast::TypeConstructorExpression expr(Source{}, &ary, ary_values);
-
-  ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(
+      array(ty.vec3<f32>(), 3, vec3<f32>(1.f, 2.f, 3.f),
+            vec3<f32>(4.f, 5.f, 6.f), vec3<f32>(7.f, 8.f, 9.f))))
+      << gen.error();
   EXPECT_EQ(gen.result(),
-            std::string("array<vec3<f32>, 3>(") + "vec3<f32>(1.0, 2.0, 3.0), " +
-                "vec3<f32>(4.0, 5.0, 6.0), " + "vec3<f32>(7.0, 8.0, 9.0))");
+            "array<vec3<f32>, 3>(vec3<f32>(1.0, 2.0, 3.0), "
+            "vec3<f32>(4.0, 5.0, 6.0), vec3<f32>(7.0, 8.0, 9.0))");
 }
 
 }  // namespace
diff --git a/src/writer/wgsl/generator_impl_continue_test.cc b/src/writer/wgsl/generator_impl_continue_test.cc
index 384acba..459bbc6 100644
--- a/src/writer/wgsl/generator_impl_continue_test.cc
+++ b/src/writer/wgsl/generator_impl_continue_test.cc
@@ -28,11 +28,11 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Continue) {
-  ast::ContinueStatement c(Source{});
+  auto* c = create<ast::ContinueStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&c)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(c)) << gen.error();
   EXPECT_EQ(gen.result(), "  continue;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_discard_test.cc b/src/writer/wgsl/generator_impl_discard_test.cc
index 52633ec..86d1c87 100644
--- a/src/writer/wgsl/generator_impl_discard_test.cc
+++ b/src/writer/wgsl/generator_impl_discard_test.cc
@@ -25,11 +25,11 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Discard) {
-  ast::DiscardStatement k(Source{});
+  auto* k = create<ast::DiscardStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&k)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(k)) << gen.error();
   EXPECT_EQ(gen.result(), "  discard;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_fallthrough_test.cc b/src/writer/wgsl/generator_impl_fallthrough_test.cc
index 454c7f8..9b4e53c 100644
--- a/src/writer/wgsl/generator_impl_fallthrough_test.cc
+++ b/src/writer/wgsl/generator_impl_fallthrough_test.cc
@@ -25,11 +25,11 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Fallthrough) {
-  ast::FallthroughStatement f(Source{});
+  auto* f = create<ast::FallthroughStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&f)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
   EXPECT_EQ(gen.result(), "  fallthrough;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_function_test.cc b/src/writer/wgsl/generator_impl_function_test.cc
index 9909f45..5e3b391 100644
--- a/src/writer/wgsl/generator_impl_function_test.cc
+++ b/src/writer/wgsl/generator_impl_function_test.cc
@@ -41,18 +41,18 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Function) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  ast::type::Void void_type;
-  ast::Function func(Source{}, mod.RegisterSymbol("my_func"), "my_func", {},
-                     &void_type, body, ast::FunctionDecorationList{});
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+      create<ast::ReturnStatement>(),
+  });
+
+  auto* func = create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func",
+                                     ast::VariableList{}, ty.void_, body,
+                                     ast::FunctionDecorationList{});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitFunction(&func));
+  ASSERT_TRUE(gen.EmitFunction(func));
   EXPECT_EQ(gen.result(), R"(  fn my_func() -> void {
     discard;
     return;
@@ -61,38 +61,22 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithParams) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::VariableList params;
-  params.push_back(
-      create<ast::Variable>(Source{},                         // source
-                            "a",                              // name
-                            ast::StorageClass::kNone,         // storage_class
-                            &f32,                             // type
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
-  params.push_back(
-      create<ast::Variable>(Source{},                         // source
-                            "b",                              // name
-                            ast::StorageClass::kNone,         // storage_class
-                            &i32,                             // type
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+      create<ast::ReturnStatement>(),
+  });
 
-  ast::type::Void void_type;
-  ast::Function func(Source{}, mod.RegisterSymbol("my_func"), "my_func", params,
-                     &void_type, body, ast::FunctionDecorationList{});
+  ast::VariableList params;
+  params.push_back(Var("a", ast::StorageClass::kNone, ty.f32));
+  params.push_back(Var("b", ast::StorageClass::kNone, ty.i32));
+
+  auto* func =
+      create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func", params,
+                            ty.void_, body, ast::FunctionDecorationList{});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitFunction(&func));
+  ASSERT_TRUE(gen.EmitFunction(func));
   EXPECT_EQ(gen.result(), R"(  fn my_func(a : f32, b : i32) -> void {
     discard;
     return;
@@ -101,21 +85,21 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithDecoration_WorkgroupSize) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  ast::type::Void void_type;
-  ast::Function func(Source{}, mod.RegisterSymbol("my_func"), "my_func", {},
-                     &void_type, body,
-                     ast::FunctionDecorationList{
-                         create<ast::WorkgroupDecoration>(Source{}, 2u, 4u, 6u),
-                     });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+      create<ast::ReturnStatement>(),
+  });
+
+  auto* func =
+      create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func",
+                            ast::VariableList{}, ty.void_, body,
+                            ast::FunctionDecorationList{
+                                create<ast::WorkgroupDecoration>(2u, 4u, 6u),
+                            });
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitFunction(&func));
+  ASSERT_TRUE(gen.EmitFunction(func));
   EXPECT_EQ(gen.result(), R"(  [[workgroup_size(2, 4, 6)]]
   fn my_func() -> void {
     discard;
@@ -125,21 +109,21 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithDecoration_Stage) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  ast::type::Void void_type;
-  ast::Function func(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", {}, &void_type, body,
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+      create<ast::ReturnStatement>(),
+  });
+
+  auto* func = create<ast::Function>(
+      mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{}, ty.void_,
+      body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitFunction(&func));
+  ASSERT_TRUE(gen.EmitFunction(func));
   EXPECT_EQ(gen.result(), R"(  [[stage(fragment)]]
   fn my_func() -> void {
     discard;
@@ -149,22 +133,22 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Function_WithDecoration_Multiple) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  ast::type::Void void_type;
-  ast::Function func(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", {}, &void_type, body,
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+      create<ast::ReturnStatement>(),
+  });
+
+  auto* func = create<ast::Function>(
+      mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{}, ty.void_,
+      body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
-          create<ast::WorkgroupDecoration>(Source{}, 2u, 4u, 6u),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
+          create<ast::WorkgroupDecoration>(2u, 4u, 6u),
       });
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitFunction(&func));
+  ASSERT_TRUE(gen.EmitFunction(func));
   EXPECT_EQ(gen.result(), R"(  [[stage(fragment)]]
   [[workgroup_size(2, 4, 6)]]
   fn my_func() -> void {
@@ -192,105 +176,74 @@
   //   return;
   // }
 
-  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{}, "d", &f32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("d", ty.f32, a_deco));
 
   ast::StructDecorationList s_decos;
-  s_decos.push_back(create<ast::StructBlockDecoration>(Source{}));
+  s_decos.push_back(create<ast::StructBlockDecoration>());
 
-  auto* str = create<ast::Struct>(Source{}, members, s_decos);
+  auto* str = create<ast::Struct>(members, 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 =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &ac,                                // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::BindingDecoration>(Source{}, 0),
-                                create<ast::SetDecoration>(Source{}, 0),
-                            });
+  auto* data_var = Var("data", ast::StorageClass::kStorageBuffer, &ac, nullptr,
+                       ast::VariableDecorationList{
+                           // decorations
+                           create<ast::BindingDecoration>(0),
+                           create<ast::SetDecoration>(0),
+                       });
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   td.RegisterVariableForTesting(data_var);
-  mod.AddGlobalVariable(data_var);
+  mod->AddGlobalVariable(data_var);
 
   {
     ast::VariableList params;
-    auto* var = create<ast::Variable>(
-        Source{},                      // source
-        "v",                           // name
-        ast::StorageClass::kFunction,  // storage_class
-        &f32,                          // type
-        false,                         // is_const
-        create<ast::MemberAccessorExpression>(
-            Source{},
-            create<ast::IdentifierExpression>(
-                Source{}, mod.RegisterSymbol("data"), "data"),
-            create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"),
-                                              "d")),  // constructor
-        ast::VariableDecorationList{});               // decorations
+    auto* var =
+        Var("v", ast::StorageClass::kFunction, ty.f32,
+            create<ast::MemberAccessorExpression>(Expr("data"), Expr("d")),
+            ast::VariableDecorationList{});
 
-    auto* body = create<ast::BlockStatement>(
-        Source{}, ast::StatementList{
-                      create<ast::VariableDeclStatement>(Source{}, var),
-                      create<ast::ReturnStatement>(Source{}),
-                  });
+    auto* body = create<ast::BlockStatement>(ast::StatementList{
+        create<ast::VariableDeclStatement>(var),
+        create<ast::ReturnStatement>(),
+    });
     auto* func = create<ast::Function>(
-        Source{}, mod.RegisterSymbol("a"), "a", params, &void_type, body,
+        mod->RegisterSymbol("a"), "a", params, ty.void_, body,
         ast::FunctionDecorationList{
-            create<ast::StageDecoration>(Source{},
-                                         ast::PipelineStage::kCompute),
+            create<ast::StageDecoration>(ast::PipelineStage::kCompute),
         });
 
-    mod.AddFunction(func);
+    mod->AddFunction(func);
   }
 
   {
     ast::VariableList params;
-    auto* var = create<ast::Variable>(
-        Source{},                      // source
-        "v",                           // name
-        ast::StorageClass::kFunction,  // storage_class
-        &f32,                          // type
-        false,                         // is_const
-        create<ast::MemberAccessorExpression>(
-            Source{},
-            create<ast::IdentifierExpression>(
-                Source{}, mod.RegisterSymbol("data"), "data"),
-            create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"),
-                                              "d")),  // constructor
-        ast::VariableDecorationList{});               // decorations
+    auto* var =
+        Var("v", ast::StorageClass::kFunction, ty.f32,
+            create<ast::MemberAccessorExpression>(Expr("data"), Expr("d")),
+            ast::VariableDecorationList{});
 
-    auto* body = create<ast::BlockStatement>(
-        Source{}, ast::StatementList{
-                      create<ast::VariableDeclStatement>(Source{}, var),
-                      create<ast::ReturnStatement>(Source{}),
-                  });
+    auto* body = create<ast::BlockStatement>(ast::StatementList{
+        create<ast::VariableDeclStatement>(var),
+        create<ast::ReturnStatement>(),
+    });
     auto* func = create<ast::Function>(
-        Source{}, mod.RegisterSymbol("b"), "b", params, &void_type, body,
+        mod->RegisterSymbol("b"), "b", params, ty.void_, body,
         ast::FunctionDecorationList{
-            create<ast::StageDecoration>(Source{},
-                                         ast::PipelineStage::kCompute),
+            create<ast::StageDecoration>(ast::PipelineStage::kCompute),
         });
 
-    mod.AddFunction(func);
+    mod->AddFunction(func);
   }
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  ASSERT_TRUE(gen.Generate(mod)) << gen.error();
+  ASSERT_TRUE(gen.Generate(*mod)) << gen.error();
   EXPECT_EQ(gen.result(), R"([[block]]
 struct Data {
   [[offset(0)]]
diff --git a/src/writer/wgsl/generator_impl_identifier_test.cc b/src/writer/wgsl/generator_impl_identifier_test.cc
index 4b50990..78f9b66 100644
--- a/src/writer/wgsl/generator_impl_identifier_test.cc
+++ b/src/writer/wgsl/generator_impl_identifier_test.cc
@@ -24,9 +24,9 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_Single) {
-  ast::IdentifierExpression i(Source{}, mod.RegisterSymbol("glsl"), "glsl");
+  auto* i = Expr("glsl");
 
-  ASSERT_TRUE(gen.EmitExpression(&i)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(i)) << gen.error();
   EXPECT_EQ(gen.result(), "glsl");
 }
 
diff --git a/src/writer/wgsl/generator_impl_if_test.cc b/src/writer/wgsl/generator_impl_if_test.cc
index 38a8f5d..2bffb23 100644
--- a/src/writer/wgsl/generator_impl_if_test.cc
+++ b/src/writer/wgsl/generator_impl_if_test.cc
@@ -28,17 +28,15 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_If) {
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  ast::IfStatement i(Source{}, cond, body, ast::ElseStatementList{});
+  auto* cond = Expr("cond");
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(cond, body, ast::ElseStatementList{});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  if (cond) {
     discard;
   }
@@ -47,25 +45,22 @@
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithElseIf) {
   auto* else_cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("else_cond"), "else_cond");
-  auto* else_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
+      mod->RegisterSymbol("else_cond"), "else_cond");
+  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  ast::IfStatement i(
-      Source{}, cond, body,
-      {create<ast::ElseStatement>(Source{}, else_cond, else_body)});
+  auto* cond = Expr("cond");
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(
+      cond, body,
+      ast::ElseStatementList{create<ast::ElseStatement>(else_cond, else_body)});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  if (cond) {
     discard;
   } elseif (else_cond) {
@@ -75,24 +70,21 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithElse) {
-  auto* else_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
+  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  ast::IfStatement i(
-      Source{}, cond, body,
-      {create<ast::ElseStatement>(Source{}, nullptr, else_body)});
+  auto* cond = Expr("cond");
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(
+      cond, body,
+      ast::ElseStatementList{create<ast::ElseStatement>(nullptr, else_body)});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  if (cond) {
     discard;
   } else {
@@ -103,34 +95,30 @@
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithMultiple) {
   auto* else_cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("else_cond"), "else_cond");
+      mod->RegisterSymbol("else_cond"), "else_cond");
 
-  auto* else_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
+  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
 
-  auto* else_body_2 = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
+  auto* else_body_2 = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  ast::IfStatement i(
-      Source{}, cond, body,
-      {
-          create<ast::ElseStatement>(Source{}, else_cond, else_body),
-          create<ast::ElseStatement>(Source{}, nullptr, else_body_2),
+  auto* cond = Expr("cond");
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(
+      cond, body,
+      ast::ElseStatementList{
+          create<ast::ElseStatement>(else_cond, else_body),
+          create<ast::ElseStatement>(nullptr, else_body_2),
       });
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(i)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  if (cond) {
     discard;
   } elseif (else_cond) {
diff --git a/src/writer/wgsl/generator_impl_loop_test.cc b/src/writer/wgsl/generator_impl_loop_test.cc
index 94cc1e1..dc2a309 100644
--- a/src/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/writer/wgsl/generator_impl_loop_test.cc
@@ -28,15 +28,14 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Loop) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  ast::LoopStatement l(Source{}, body, {});
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* l = create<ast::LoopStatement>(body, nullptr);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&l)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  loop {
     discard;
   }
@@ -44,19 +43,17 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_LoopWithContinuing) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  auto* continuing = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  ast::LoopStatement l(Source{}, body, continuing);
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* continuing = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* l = create<ast::LoopStatement>(body, continuing);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&l)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  loop {
     discard;
 
diff --git a/src/writer/wgsl/generator_impl_member_accessor_test.cc b/src/writer/wgsl/generator_impl_member_accessor_test.cc
index d01c0ce..d8c4f39 100644
--- a/src/writer/wgsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/wgsl/generator_impl_member_accessor_test.cc
@@ -28,14 +28,12 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_MemberAccessor) {
-  auto* str = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("str"), "str");
-  auto* mem = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mem"), "mem");
+  auto* str = Expr("str");
+  auto* mem = Expr("mem");
 
-  ast::MemberAccessorExpression expr(Source{}, str, mem);
+  auto* expr = create<ast::MemberAccessorExpression>(str, mem);
 
-  ASSERT_TRUE(gen.EmitExpression(&expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(expr)) << gen.error();
   EXPECT_EQ(gen.result(), "str.mem");
 }
 
diff --git a/src/writer/wgsl/generator_impl_return_test.cc b/src/writer/wgsl/generator_impl_return_test.cc
index 961e484..ba27df8 100644
--- a/src/writer/wgsl/generator_impl_return_test.cc
+++ b/src/writer/wgsl/generator_impl_return_test.cc
@@ -29,22 +29,21 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Return) {
-  ast::ReturnStatement r(Source{});
+  auto* r = create<ast::ReturnStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&r)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
   EXPECT_EQ(gen.result(), "  return;\n");
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_ReturnWithValue) {
-  auto* expr = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("expr"), "expr");
-  ast::ReturnStatement r(Source{}, expr);
+  auto* expr = Expr("expr");
+  auto* r = create<ast::ReturnStatement>(expr);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&r)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(r)) << gen.error();
   EXPECT_EQ(gen.result(), "  return expr;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_switch_test.cc b/src/writer/wgsl/generator_impl_switch_test.cc
index 495d924..602fe51 100644
--- a/src/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/writer/wgsl/generator_impl_switch_test.cc
@@ -32,35 +32,30 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Switch) {
-  auto* def_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::BreakStatement>(Source{}),
-                });
-  auto* def =
-      create<ast::CaseStatement>(Source{}, ast::CaseSelectorList{}, def_body);
+  auto* def_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::BreakStatement>(),
+  });
+  auto* def = create<ast::CaseStatement>(ast::CaseSelectorList{}, def_body);
 
-  ast::type::I32 i32;
   ast::CaseSelectorList case_val;
-  case_val.push_back(create<ast::SintLiteral>(Source{}, &i32, 5));
+  case_val.push_back(create<ast::SintLiteral>(ty.i32, 5));
 
-  auto* case_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::BreakStatement>(Source{}),
-                });
+  auto* case_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::BreakStatement>(),
+  });
 
-  auto* case_stmt = create<ast::CaseStatement>(Source{}, case_val, case_body);
+  auto* case_stmt = create<ast::CaseStatement>(case_val, case_body);
 
   ast::CaseStatementList body;
   body.push_back(case_stmt);
   body.push_back(def);
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  ast::SwitchStatement s(Source{}, cond, body);
+  auto* cond = Expr("cond");
+  auto* s = create<ast::SwitchStatement>(cond, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&s)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(s)) << gen.error();
   EXPECT_EQ(gen.result(), R"(  switch(cond) {
     case 5: {
       break;
diff --git a/src/writer/wgsl/generator_impl_test.cc b/src/writer/wgsl/generator_impl_test.cc
index f2ea0f3..abc82a7 100644
--- a/src/writer/wgsl/generator_impl_test.cc
+++ b/src/writer/wgsl/generator_impl_test.cc
@@ -32,12 +32,12 @@
 TEST_F(WgslGeneratorImplTest, Generate) {
   ast::type::Void void_type;
 
-  mod.AddFunction(create<ast::Function>(
-      Source{}, mod.RegisterSymbol("a_func"), "my_func", ast::VariableList{},
-      &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+  mod->AddFunction(create<ast::Function>(
+      mod->RegisterSymbol("a_func"), "my_func", ast::VariableList{}, &void_type,
+      create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{}));
 
-  ASSERT_TRUE(gen.Generate(mod)) << gen.error();
+  ASSERT_TRUE(gen.Generate(*mod)) << gen.error();
   EXPECT_EQ(gen.result(), R"(fn my_func() -> void {
 }
 
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc
index ff533a8..4f24629 100644
--- a/src/writer/wgsl/generator_impl_type_test.cc
+++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -48,66 +48,59 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitType_Alias) {
-  ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("alias"), "alias", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("alias"), "alias", ty.f32);
 
   ASSERT_TRUE(gen.EmitType(&alias)) << gen.error();
   EXPECT_EQ(gen.result(), "alias");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Array) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(&a)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.array<bool, 4>())) << gen.error();
   EXPECT_EQ(gen.result(), "array<bool, 4>");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_Read) {
-  ast::type::I32 i32;
-
-  ast::StructMember mem(Source{}, "a", &i32, ast::StructMemberDecorationList{});
+  auto* mem =
+      create<ast::StructMember>("a", ty.i32, ast::StructMemberDecorationList{});
   ast::StructMemberList members;
-  members.push_back(&mem);
+  members.push_back(mem);
 
-  ast::StructBlockDecoration block_deco(Source{});
+  auto* block_deco = create<ast::StructBlockDecoration>();
   ast::StructDecorationList decos;
-  decos.push_back(&block_deco);
+  decos.push_back(block_deco);
 
-  ast::Struct str(Source{}, members, decos);
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", &str);
+  auto* str = create<ast::Struct>(members, decos);
+  auto* s = create<ast::type::Struct>(mod->RegisterSymbol("S"), "S", str);
 
-  ast::type::AccessControl a(ast::AccessControl::kReadOnly, &s);
+  ast::type::AccessControl a(ast::AccessControl::kReadOnly, s);
 
   ASSERT_TRUE(gen.EmitType(&a)) << gen.error();
   EXPECT_EQ(gen.result(), "[[access(read)]]\nS");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_ReadWrite) {
-  ast::type::I32 i32;
-
-  ast::StructMember mem(Source{}, "a", &i32, ast::StructMemberDecorationList{});
+  auto* mem =
+      create<ast::StructMember>("a", ty.i32, ast::StructMemberDecorationList{});
   ast::StructMemberList members;
-  members.push_back(&mem);
+  members.push_back(mem);
 
-  ast::StructBlockDecoration block_deco(Source{});
+  auto* block_deco = create<ast::StructBlockDecoration>();
   ast::StructDecorationList decos;
-  decos.push_back(&block_deco);
+  decos.push_back(block_deco);
 
-  ast::Struct str(Source{}, members, decos);
-  ast::type::Struct s(mod.RegisterSymbol("S"), "S", &str);
+  auto* str = create<ast::Struct>(members, decos);
+  auto* s = create<ast::type::Struct>(mod->RegisterSymbol("S"), "S", str);
 
-  ast::type::AccessControl a(ast::AccessControl::kReadWrite, &s);
+  ast::type::AccessControl a(ast::AccessControl::kReadWrite, s);
 
   ASSERT_TRUE(gen.EmitType(&a)) << gen.error();
   EXPECT_EQ(gen.result(), "[[access(read_write)]]\nS");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Array_Decoration) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4,
+  ast::type::Array a(ty.bool_, 4,
                      ast::ArrayDecorationList{
-                         create<ast::StrideDecoration>(Source{}, 16u),
+                         create<ast::StrideDecoration>(16u),
                      });
 
   ASSERT_TRUE(gen.EmitType(&a)) << gen.error();
@@ -115,11 +108,10 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Array_MultipleDecorations) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4,
+  ast::type::Array a(ty.bool_, 4,
                      ast::ArrayDecorationList{
-                         create<ast::StrideDecoration>(Source{}, 16u),
-                         create<ast::StrideDecoration>(Source{}, 32u),
+                         create<ast::StrideDecoration>(16u),
+                         create<ast::StrideDecoration>(32u),
                      });
 
   ASSERT_TRUE(gen.EmitType(&a)) << gen.error();
@@ -127,87 +119,68 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_RuntimeArray) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 0, ast::ArrayDecorationList{});
+  ast::type::Array a(ty.bool_, 0, ast::ArrayDecorationList{});
 
   ASSERT_TRUE(gen.EmitType(&a)) << gen.error();
   EXPECT_EQ(gen.result(), "array<bool>");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Bool) {
-  ast::type::Bool b;
-
-  ASSERT_TRUE(gen.EmitType(&b)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.bool_)) << gen.error();
   EXPECT_EQ(gen.result(), "bool");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_F32) {
-  ast::type::F32 f32;
-
-  ASSERT_TRUE(gen.EmitType(&f32)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.f32)) << gen.error();
   EXPECT_EQ(gen.result(), "f32");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_I32) {
-  ast::type::I32 i32;
-
-  ASSERT_TRUE(gen.EmitType(&i32)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.i32)) << gen.error();
   EXPECT_EQ(gen.result(), "i32");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Matrix) {
-  ast::type::F32 f32;
-  ast::type::Matrix m(&f32, 3, 2);
-
-  ASSERT_TRUE(gen.EmitType(&m)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.mat2x3<f32>())) << gen.error();
   EXPECT_EQ(gen.result(), "mat2x3<f32>");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Pointer) {
-  ast::type::F32 f32;
-  ast::type::Pointer p(&f32, ast::StorageClass::kWorkgroup);
+  ast::type::Pointer p(ty.f32, ast::StorageClass::kWorkgroup);
 
   ASSERT_TRUE(gen.EmitType(&p)) << gen.error();
   EXPECT_EQ(gen.result(), "ptr<workgroup, f32>");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "a", &i32, ast::StructMemberDecorationList{}));
+      "a", ty.i32, ast::StructMemberDecorationList{}));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &f32, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("b", ty.f32, b_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(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(WgslGeneratorImplTest, EmitType_StructDecl) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "a", &i32, ast::StructMemberDecorationList{}));
+      "a", ty.i32, ast::StructMemberDecorationList{}));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &f32, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("b", ty.f32, b_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(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 {
@@ -219,23 +192,20 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "a", &i32, ast::StructMemberDecorationList{}));
+      "a", ty.i32, ast::StructMemberDecorationList{}));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &f32, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("b", ty.f32, b_deco));
 
   ast::StructDecorationList decos;
-  decos.push_back(create<ast::StructBlockDecoration>(Source{}));
+  decos.push_back(create<ast::StructBlockDecoration>());
 
-  auto* str = create<ast::Struct>(Source{}, members, decos);
+  auto* str = create<ast::Struct>(members, decos);
 
-  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"([[block]]
@@ -248,17 +218,12 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_U32) {
-  ast::type::U32 u32;
-
-  ASSERT_TRUE(gen.EmitType(&u32)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.u32)) << gen.error();
   EXPECT_EQ(gen.result(), "u32");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Vector) {
-  ast::type::F32 f32;
-  ast::type::Vector v(&f32, 3);
-
-  ASSERT_TRUE(gen.EmitType(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(ty.vec3<f32>())) << gen.error();
   EXPECT_EQ(gen.result(), "vec3<f32>");
 }
 
@@ -302,8 +267,7 @@
 TEST_P(WgslGenerator_SampledTextureTest, EmitType_SampledTexture_F32) {
   auto param = GetParam();
 
-  ast::type::F32 f32;
-  ast::type::SampledTexture t(param.dim, &f32);
+  ast::type::SampledTexture t(param.dim, ty.f32);
 
   ASSERT_TRUE(gen.EmitType(&t)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(param.name) + "<f32>");
@@ -312,8 +276,7 @@
 TEST_P(WgslGenerator_SampledTextureTest, EmitType_SampledTexture_I32) {
   auto param = GetParam();
 
-  ast::type::I32 i32;
-  ast::type::SampledTexture t(param.dim, &i32);
+  ast::type::SampledTexture t(param.dim, ty.i32);
 
   ASSERT_TRUE(gen.EmitType(&t)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(param.name) + "<i32>");
@@ -322,8 +285,7 @@
 TEST_P(WgslGenerator_SampledTextureTest, EmitType_SampledTexture_U32) {
   auto param = GetParam();
 
-  ast::type::U32 u32;
-  ast::type::SampledTexture t(param.dim, &u32);
+  ast::type::SampledTexture t(param.dim, ty.u32);
 
   ASSERT_TRUE(gen.EmitType(&t)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(param.name) + "<u32>");
@@ -345,8 +307,7 @@
 TEST_P(WgslGenerator_MultiampledTextureTest, EmitType_MultisampledTexture_F32) {
   auto param = GetParam();
 
-  ast::type::F32 f32;
-  ast::type::MultisampledTexture t(param.dim, &f32);
+  ast::type::MultisampledTexture t(param.dim, ty.f32);
 
   ASSERT_TRUE(gen.EmitType(&t)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(param.name) + "<f32>");
@@ -355,8 +316,7 @@
 TEST_P(WgslGenerator_MultiampledTextureTest, EmitType_MultisampledTexture_I32) {
   auto param = GetParam();
 
-  ast::type::I32 i32;
-  ast::type::MultisampledTexture t(param.dim, &i32);
+  ast::type::MultisampledTexture t(param.dim, ty.i32);
 
   ASSERT_TRUE(gen.EmitType(&t)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(param.name) + "<i32>");
@@ -365,8 +325,7 @@
 TEST_P(WgslGenerator_MultiampledTextureTest, EmitType_MultisampledTexture_U32) {
   auto param = GetParam();
 
-  ast::type::U32 u32;
-  ast::type::MultisampledTexture t(param.dim, &u32);
+  ast::type::MultisampledTexture t(param.dim, ty.u32);
 
   ASSERT_TRUE(gen.EmitType(&t)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(param.name) + "<u32>");
diff --git a/src/writer/wgsl/generator_impl_unary_op_test.cc b/src/writer/wgsl/generator_impl_unary_op_test.cc
index 7f8acd4..16aa616 100644
--- a/src/writer/wgsl/generator_impl_unary_op_test.cc
+++ b/src/writer/wgsl/generator_impl_unary_op_test.cc
@@ -38,11 +38,10 @@
 TEST_P(WgslUnaryOpTest, Emit) {
   auto params = GetParam();
 
-  auto* expr = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("expr"), "expr");
-  ast::UnaryOpExpression op(Source{}, params.op, expr);
+  auto* expr = Expr("expr");
+  auto* op = create<ast::UnaryOpExpression>(params.op, expr);
 
-  ASSERT_TRUE(gen.EmitExpression(&op)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(op)) << gen.error();
   EXPECT_EQ(gen.result(), std::string(params.name) + "(expr)");
 }
 INSTANTIATE_TEST_SUITE_P(WgslGeneratorImplTest,
diff --git a/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc b/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
index c7a11bc..3071b8c 100644
--- a/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
@@ -31,21 +31,13 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement) {
-  ast::type::F32 f32;
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &f32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* var = Var("a", ast::StorageClass::kNone, ty.f32);
 
-  ast::VariableDeclStatement stmt(Source{}, var);
+  auto* stmt = create<ast::VariableDeclStatement>(var);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
   EXPECT_EQ(gen.result(), "  var a : f32;\n");
 }
 
@@ -53,40 +45,25 @@
   // Variable declarations with Function storage class don't mention their
   // storage class.  Rely on defaulting.
   // https://github.com/gpuweb/gpuweb/issues/654
-  ast::type::F32 f32;
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &f32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
 
-  ast::VariableDeclStatement stmt(Source{}, var);
+  auto* var = Var("a", ast::StorageClass::kFunction, ty.f32);
+
+  auto* stmt = create<ast::VariableDeclStatement>(var);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
   EXPECT_EQ(gen.result(), "  var a : f32;\n");
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
-  ast::type::F32 f32;
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kPrivate,     // storage_class
-                            &f32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* var = Var("a", ast::StorageClass::kPrivate, ty.f32);
 
-  ast::VariableDeclStatement stmt(Source{}, var);
+  auto* stmt = create<ast::VariableDeclStatement>(var);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(&stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
   EXPECT_EQ(gen.result(), "  var<private> a : f32;\n");
 }
 
diff --git a/src/writer/wgsl/generator_impl_variable_test.cc b/src/writer/wgsl/generator_impl_variable_test.cc
index 3c9878c..cbe77f6 100644
--- a/src/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_test.cc
@@ -34,52 +34,43 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitVariable) {
-  ast::type::F32 f32;
-  ast::Variable v(Source{}, "a", ast::StorageClass::kNone, &f32, false, nullptr,
-                  ast::VariableDecorationList{});
+  auto* v = Var("a", ast::StorageClass::kNone, ty.f32);
 
-  ASSERT_TRUE(gen.EmitVariable(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(gen.result(), R"(var a : f32;
 )");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_StorageClass) {
-  ast::type::F32 f32;
-  ast::Variable v(Source{}, "a", ast::StorageClass::kInput, &f32, false,
-                  nullptr, ast::VariableDecorationList{});
+  auto* v = Var("a", ast::StorageClass::kInput, ty.f32);
 
-  ASSERT_TRUE(gen.EmitVariable(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(gen.result(), R"(var<in> a : f32;
 )");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated) {
-  ast::type::F32 f32;
+  auto* v = Var("a", ast::StorageClass::kNone, ty.f32, nullptr,
+                ast::VariableDecorationList{
+                    create<ast::LocationDecoration>(2),
+                });
 
-  ast::Variable v(Source{}, "a", ast::StorageClass::kNone, &f32, false, nullptr,
-                  ast::VariableDecorationList{
-                      create<ast::LocationDecoration>(Source{}, 2),
-                  });
-
-  ASSERT_TRUE(gen.EmitVariable(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(gen.result(), R"([[location(2)]] var a : f32;
 )");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated_Multiple) {
-  ast::type::F32 f32;
+  auto* v = Var("a", ast::StorageClass::kNone, ty.f32, nullptr,
+                ast::VariableDecorationList{
+                    create<ast::BuiltinDecoration>(ast::Builtin::kPosition),
+                    create<ast::BindingDecoration>(0),
+                    create<ast::SetDecoration>(1),
+                    create<ast::LocationDecoration>(2),
+                    create<ast::ConstantIdDecoration>(42),
+                });
 
-  ast::Variable v(
-      Source{}, "a", ast::StorageClass::kNone, &f32, false, nullptr,
-      ast::VariableDecorationList{
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kPosition),
-          create<ast::BindingDecoration>(Source{}, 0),
-          create<ast::SetDecoration>(Source{}, 1),
-          create<ast::LocationDecoration>(Source{}, 2),
-          create<ast::ConstantIdDecoration>(Source{}, 42),
-      });
-
-  ASSERT_TRUE(gen.EmitVariable(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(
       gen.result(),
       R"([[builtin(position), binding(0), set(1), location(2), constant_id(42)]] var a : f32;
@@ -88,26 +79,24 @@
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Constructor) {
   auto* ident = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("initializer"), "initializer");
+      mod->RegisterSymbol("initializer"), "initializer");
 
-  ast::type::F32 f32;
-  ast::Variable v(Source{}, "a", ast::StorageClass::kNone, &f32, false, ident,
-                  ast::VariableDecorationList{});
+  auto* v = Var("a", ast::StorageClass::kNone, ty.f32, ident,
+                ast::VariableDecorationList{});
 
-  ASSERT_TRUE(gen.EmitVariable(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(gen.result(), R"(var a : f32 = initializer;
 )");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Const) {
   auto* ident = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("initializer"), "initializer");
+      mod->RegisterSymbol("initializer"), "initializer");
 
-  ast::type::F32 f32;
-  ast::Variable v(Source{}, "a", ast::StorageClass::kNone, &f32, true, ident,
+  auto* v = Const("a", ast::StorageClass::kNone, ty.f32, ident,
                   ast::VariableDecorationList{});
 
-  ASSERT_TRUE(gen.EmitVariable(&v)) << gen.error();
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(gen.result(), R"(const a : f32 = initializer;
 )");
 }
diff --git a/src/writer/wgsl/test_helper.h b/src/writer/wgsl/test_helper.h
index 4b8055e..d6c5a93 100644
--- a/src/writer/wgsl/test_helper.h
+++ b/src/writer/wgsl/test_helper.h
@@ -19,7 +19,7 @@
 #include <utility>
 
 #include "gtest/gtest.h"
-#include "src/ast/module.h"
+#include "src/ast/builder.h"
 #include "src/type_determiner.h"
 #include "src/writer/wgsl/generator_impl.h"
 
@@ -29,23 +29,12 @@
 
 /// Helper class for testing
 template <typename BASE>
-class TestHelperBase : public BASE {
+class TestHelperBase : public BASE, public ast::BuilderWithModule {
  public:
-  TestHelperBase() : td(&mod), gen() {}
+  TestHelperBase() : td(mod), gen() {}
 
   ~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