Start cleaning up tests (5/N)

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

Change-Id: Ife7da25a4171cce404d496cb63acc34522316d81
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/35742
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/writer/hlsl/generator_impl_alias_type_test.cc b/src/writer/hlsl/generator_impl_alias_type_test.cc
index a0c53a7..1bda70d 100644
--- a/src/writer/hlsl/generator_impl_alias_type_test.cc
+++ b/src/writer/hlsl/generator_impl_alias_type_test.cc
@@ -29,8 +29,7 @@
 using HlslGeneratorImplTest_Alias = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Alias, 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(out, &alias)) << gen.error();
   EXPECT_EQ(result(), R"(typedef float a;
@@ -38,8 +37,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_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(out, &alias)) << gen.error();
   EXPECT_EQ(result(), R"(typedef float float_tint_0;
@@ -47,23 +45,20 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Alias, EmitAlias_Struct) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   auto* str = create<ast::Struct>(
-      Source{},
+
       ast::StructMemberList{
-          create<ast::StructMember>(Source{}, "a", &f32,
+          create<ast::StructMember>("a", ty.f32,
                                     ast::StructMemberDecorationList{}),
           create<ast::StructMember>(
-              Source{}, "b", &i32,
+              "b", ty.i32,
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 4)}),
+                  create<ast::StructMemberOffsetDecoration>(4)}),
       },
       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(out, &alias)) << gen.error();
   EXPECT_EQ(result(), R"(struct B {
diff --git a/src/writer/hlsl/generator_impl_array_accessor_test.cc b/src/writer/hlsl/generator_impl_array_accessor_test.cc
index 74c2ac1..7e628fd 100644
--- a/src/writer/hlsl/generator_impl_array_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_array_accessor_test.cc
@@ -30,27 +30,16 @@
 using HlslGeneratorImplTest_Expression = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Expression, 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* expr = Index("ary", 5);
 
-  ast::ArrayAccessorExpression expr(Source{}, ary, idx);
-
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "ary[5]");
 }
 
 TEST_F(HlslGeneratorImplTest_Expression, EmitArrayAccessor) {
-  auto* ary = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("ary"), "ary");
-  auto* idx = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("idx"), "idx");
+  auto* expr = Index("ary", "idx");
 
-  ast::ArrayAccessorExpression expr(Source{}, ary, idx);
-
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "ary[idx]");
 }
 
diff --git a/src/writer/hlsl/generator_impl_assign_test.cc b/src/writer/hlsl/generator_impl_assign_test.cc
index fcf37e8..549ce5e 100644
--- a/src/writer/hlsl/generator_impl_assign_test.cc
+++ b/src/writer/hlsl/generator_impl_assign_test.cc
@@ -28,15 +28,13 @@
 using HlslGeneratorImplTest_Assign = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Assign, 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(out, &assign)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(), "  lhs = rhs;\n");
 }
 
diff --git a/src/writer/hlsl/generator_impl_binary_test.cc b/src/writer/hlsl/generator_impl_binary_test.cc
index 9b7a4a0..cce256a 100644
--- a/src/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/writer/hlsl/generator_impl_binary_test.cc
@@ -58,111 +58,57 @@
 
 using HlslBinaryTest = TestParamHelper<BinaryData>;
 TEST_P(HlslBinaryTest, Emit_f32) {
-  ast::type::F32 f32;
-
   auto params = GetParam();
 
-  auto* left_var =
-      create<ast::Variable>(Source{},                        // source
-                            "left",                          // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &f32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* right_var =
-      create<ast::Variable>(Source{},                        // source
-                            "right",                         // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &f32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* left_var = Var("left", ast::StorageClass::kFunction, ty.f32);
+  auto* right_var = Var("right", ast::StorageClass::kFunction, ty.f32);
 
-  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");
 
   td.RegisterVariableForTesting(left_var);
   td.RegisterVariableForTesting(right_var);
 
-  ast::BinaryExpression expr(Source{}, params.op, left, right);
+  auto* expr = create<ast::BinaryExpression>(params.op, left, right);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), params.result);
 }
 TEST_P(HlslBinaryTest, Emit_u32) {
-  ast::type::U32 u32;
-
   auto params = GetParam();
 
-  auto* left_var =
-      create<ast::Variable>(Source{},                        // source
-                            "left",                          // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &u32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* right_var =
-      create<ast::Variable>(Source{},                        // source
-                            "right",                         // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &u32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* left_var = Var("left", ast::StorageClass::kFunction, ty.u32);
+  auto* right_var = Var("right", ast::StorageClass::kFunction, ty.u32);
 
-  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");
 
   td.RegisterVariableForTesting(left_var);
   td.RegisterVariableForTesting(right_var);
 
-  ast::BinaryExpression expr(Source{}, params.op, left, right);
+  auto* expr = create<ast::BinaryExpression>(params.op, left, right);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), params.result);
 }
 TEST_P(HlslBinaryTest, Emit_i32) {
-  ast::type::I32 i32;
-
   auto params = GetParam();
 
-  auto* left_var =
-      create<ast::Variable>(Source{},                        // source
-                            "left",                          // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &i32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* right_var =
-      create<ast::Variable>(Source{},                        // source
-                            "right",                         // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &i32,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* left_var = Var("left", ast::StorageClass::kFunction, ty.i32);
+  auto* right_var = Var("right", ast::StorageClass::kFunction, ty.i32);
 
-  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");
 
   td.RegisterVariableForTesting(left_var);
   td.RegisterVariableForTesting(right_var);
 
-  ast::BinaryExpression expr(Source{}, params.op, left, right);
+  auto* expr = create<ast::BinaryExpression>(params.op, left, right);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -187,214 +133,116 @@
         BinaryData{"(left % right)", ast::BinaryOp::kModulo}));
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_VectorScalar) {
-  ast::type::F32 f32;
-  ast::type::Vector vec3(&f32, 3);
+  auto* lhs = vec3<f32>(1.f, 1.f, 1.f);
+  auto* rhs = Expr(1.f);
 
-  auto* lhs = create<ast::TypeConstructorExpression>(
-      Source{}, &vec3,
-      ast::ExpressionList{
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)),
-      });
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  auto* rhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f));
-
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             "(float3(1.0f, 1.0f, 1.0f) * "
             "1.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarVector) {
-  ast::type::F32 f32;
-  ast::type::Vector vec3(&f32, 3);
+  auto* lhs = Expr(1.f);
+  auto* rhs = vec3<f32>(1.f, 1.f, 1.f);
 
-  auto* lhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f));
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  ast::ExpressionList vals;
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  auto* rhs = create<ast::TypeConstructorExpression>(Source{}, &vec3, vals);
-
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             "(1.0f * float3(1.0f, 1.0f, "
             "1.0f))");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixScalar) {
-  ast::type::F32 f32;
-  ast::type::Matrix mat3(&f32, 3, 3);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "mat",                           // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &mat3,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mat"), "mat");
-  auto* rhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f));
+  auto* var = Var("mat", ast::StorageClass::kFunction, ty.mat3x3<f32>());
+  auto* lhs = Expr("mat");
+  auto* rhs = Expr(1.f);
 
   td.RegisterVariableForTesting(var);
 
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "(mat * 1.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarMatrix) {
-  ast::type::F32 f32;
-  ast::type::Matrix mat3(&f32, 3, 3);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "mat",                           // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &mat3,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* lhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f));
-  auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mat"), "mat");
+  auto* var = Var("mat", ast::StorageClass::kFunction, ty.mat3x3<f32>());
+  auto* lhs = Expr(1.f);
+  auto* rhs = Expr("mat");
 
   td.RegisterVariableForTesting(var);
 
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "(1.0f * mat)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixVector) {
-  ast::type::F32 f32;
-  ast::type::Vector vec3(&f32, 3);
-  ast::type::Matrix mat3(&f32, 3, 3);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "mat",                           // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &mat3,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mat"), "mat");
-
-  ast::ExpressionList vals;
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  auto* rhs = create<ast::TypeConstructorExpression>(Source{}, &vec3, vals);
+  auto* var = Var("mat", ast::StorageClass::kFunction, ty.mat3x3<f32>());
+  auto* lhs = Expr("mat");
+  auto* rhs = vec3<f32>(1.f, 1.f, 1.f);
 
   td.RegisterVariableForTesting(var);
 
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "mul(mat, float3(1.0f, 1.0f, 1.0f))");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_VectorMatrix) {
-  ast::type::F32 f32;
-  ast::type::Vector vec3(&f32, 3);
-  ast::type::Matrix mat3(&f32, 3, 3);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "mat",                           // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &mat3,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-
-  ast::ExpressionList vals;
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  vals.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  auto* lhs = create<ast::TypeConstructorExpression>(Source{}, &vec3, vals);
-
-  auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mat"), "mat");
+  auto* var = Var("mat", ast::StorageClass::kFunction, ty.mat3x3<f32>());
+  auto* lhs = vec3<f32>(1.f, 1.f, 1.f);
+  auto* rhs = Expr("mat");
 
   td.RegisterVariableForTesting(var);
 
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "mul(float3(1.0f, 1.0f, 1.0f), mat)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixMatrix) {
-  ast::type::F32 f32;
-  ast::type::Vector vec3(&f32, 3);
-  ast::type::Matrix mat3(&f32, 3, 3);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "mat",                           // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &mat3,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mat"), "mat");
-  auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mat"), "mat");
+  auto* var = Var("mat", ast::StorageClass::kFunction, ty.mat3x3<f32>());
+  auto* lhs = Expr("mat");
+  auto* rhs = Expr("mat");
 
   td.RegisterVariableForTesting(var);
 
-  ast::BinaryExpression expr(Source{}, ast::BinaryOp::kMultiply, lhs, rhs);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  EXPECT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "mul(mat, mat)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_And) {
-  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{}, ast::BinaryOp::kLogicalAnd, left, right);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, left, right);
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "(_tint_tmp)");
   EXPECT_EQ(pre_result(), R"(bool _tint_tmp = left;
 if (_tint_tmp) {
@@ -405,21 +253,17 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_Multi) {
   // (a && b) || (c || d)
-  auto* a =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"), "a");
-  auto* b =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"), "b");
-  auto* c =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"), "c");
-  auto* d =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"), "d");
+  auto* a = Expr("a");
+  auto* b = Expr("b");
+  auto* c = Expr("c");
+  auto* d = Expr("d");
 
-  ast::BinaryExpression expr(
-      Source{}, ast::BinaryOp::kLogicalOr,
-      create<ast::BinaryExpression>(Source{}, ast::BinaryOp::kLogicalAnd, a, b),
-      create<ast::BinaryExpression>(Source{}, ast::BinaryOp::kLogicalOr, c, d));
+  auto* expr = create<ast::BinaryExpression>(
+      ast::BinaryOp::kLogicalOr,
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, a, b),
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, c, d));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "(_tint_tmp_0)");
   EXPECT_EQ(pre_result(), R"(bool _tint_tmp = a;
 if (_tint_tmp) {
@@ -437,14 +281,13 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_Or) {
-  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{}, ast::BinaryOp::kLogicalOr, left, right);
+  auto* expr =
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, left, right);
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "(_tint_tmp)");
   EXPECT_EQ(pre_result(), R"(bool _tint_tmp = left;
 if (!_tint_tmp) {
@@ -462,56 +305,33 @@
   //   return 3;
   // }
 
-  ast::type::I32 i32;
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(Expr(3)),
+  });
+  auto* else_stmt = create<ast::ElseStatement>(nullptr, body);
 
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(
-                        Source{}, create<ast::ScalarConstructorExpression>(
-                                      Source{}, create<ast::SintLiteral>(
-                                                    Source{}, &i32, 3))),
-                });
-  auto* else_stmt = create<ast::ElseStatement>(Source{}, nullptr, body);
-
-  body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(
-                        Source{}, create<ast::ScalarConstructorExpression>(
-                                      Source{}, create<ast::SintLiteral>(
-                                                    Source{}, &i32, 2))),
-                });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(Expr(2)),
+  });
   auto* else_if_stmt = create<ast::ElseStatement>(
-      Source{},
-      create<ast::BinaryExpression>(
-          Source{}, ast::BinaryOp::kLogicalOr,
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"),
-                                            "c")),
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"),
+                                    Expr("c")),
       body);
 
-  body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(
-                        Source{}, create<ast::ScalarConstructorExpression>(
-                                      Source{}, create<ast::SintLiteral>(
-                                                    Source{}, &i32, 1))),
-                });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(Expr(1)),
+  });
 
-  ast::IfStatement expr(Source{},
-                        create<ast::BinaryExpression>(
-                            Source{}, ast::BinaryOp::kLogicalAnd,
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("a"), "a"),
-                            create<ast::IdentifierExpression>(
-                                Source{}, mod.RegisterSymbol("b"), "b")),
-                        body,
-                        {
-                            else_if_stmt,
-                            else_stmt,
-                        });
+  auto* expr = create<ast::IfStatement>(
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"),
+                                    Expr("b")),
+      body,
+      ast::ElseStatementList{
+          else_if_stmt,
+          else_stmt,
+      });
 
-  ASSERT_TRUE(gen.EmitStatement(out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, expr)) << gen.error();
   EXPECT_EQ(result(), R"(bool _tint_tmp = a;
 if (_tint_tmp) {
   _tint_tmp = b;
@@ -534,21 +354,15 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Return_WithLogical) {
   // return (a && b) || c;
-  auto* a =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"), "a");
-  auto* b =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"), "b");
-  auto* c =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"), "c");
+  auto* a = Expr("a");
+  auto* b = Expr("b");
+  auto* c = Expr("c");
 
-  ast::ReturnStatement expr(Source{},
-                            create<ast::BinaryExpression>(
-                                Source{}, ast::BinaryOp::kLogicalOr,
-                                create<ast::BinaryExpression>(
-                                    Source{}, ast::BinaryOp::kLogicalAnd, a, b),
-                                c));
+  auto* expr = create<ast::ReturnStatement>(create<ast::BinaryExpression>(
+      ast::BinaryOp::kLogicalOr,
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, a, b), c));
 
-  ASSERT_TRUE(gen.EmitStatement(out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, expr)) << gen.error();
   EXPECT_EQ(result(), R"(bool _tint_tmp = a;
 if (_tint_tmp) {
   _tint_tmp = b;
@@ -563,24 +377,18 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Assign_WithLogical) {
   // a = (b || c) && d;
-  auto* a =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"), "a");
-  auto* b =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"), "b");
-  auto* c =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"), "c");
-  auto* d =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"), "d");
+  auto* a = Expr("a");
+  auto* b = Expr("b");
+  auto* c = Expr("c");
+  auto* d = Expr("d");
 
-  ast::AssignmentStatement expr(
-      Source{}, a,
+  auto* expr = create<ast::AssignmentStatement>(
+      a,
       create<ast::BinaryExpression>(
-          Source{}, ast::BinaryOp::kLogicalAnd,
-          create<ast::BinaryExpression>(Source{}, ast::BinaryOp::kLogicalOr, b,
-                                        c),
-          d));
+          ast::BinaryOp::kLogicalAnd,
+          create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, b, c), d));
 
-  ASSERT_TRUE(gen.EmitStatement(out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, expr)) << gen.error();
   EXPECT_EQ(result(), R"(bool _tint_tmp = b;
 if (!_tint_tmp) {
   _tint_tmp = c;
@@ -595,31 +403,21 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Decl_WithLogical) {
   // var a : bool = (b && c) || d;
-  ast::type::Bool bool_type;
 
-  auto* b =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"), "b");
-  auto* c =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"), "c");
-  auto* d =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"), "d");
+  auto* b = Expr("b");
+  auto* c = Expr("c");
+  auto* d = Expr("d");
 
-  auto* var =
-      create<ast::Variable>(Source{},                      // source
-                            "a",                           // name
-                            ast::StorageClass::kFunction,  // storage_class
-                            &bool_type,                    // type
-                            false,                         // is_const
-                            create<ast::BinaryExpression>(
-                                Source{}, ast::BinaryOp::kLogicalOr,
-                                create<ast::BinaryExpression>(
-                                    Source{}, ast::BinaryOp::kLogicalAnd, b, c),
-                                d),                          // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* var = Var(
+      "a", ast::StorageClass::kFunction, ty.bool_,
+      create<ast::BinaryExpression>(
+          ast::BinaryOp::kLogicalOr,
+          create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, b, c), d),
+      ast::VariableDecorationList{});
 
-  ast::VariableDeclStatement expr(Source{}, var);
+  auto* expr = create<ast::VariableDeclStatement>(var);
 
-  ASSERT_TRUE(gen.EmitStatement(out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, expr)) << gen.error();
   EXPECT_EQ(result(), R"(bool _tint_tmp = b;
 if (_tint_tmp) {
   _tint_tmp = c;
@@ -634,23 +432,18 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Bitcast_WithLogical) {
   // as<i32>(a && (b || c))
-  ast::type::I32 i32;
 
-  auto* a =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"), "a");
-  auto* b =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"), "b");
-  auto* c =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"), "c");
+  auto* a = Expr("a");
+  auto* b = Expr("b");
+  auto* c = Expr("c");
 
-  ast::BitcastExpression expr(
-      Source{}, &i32,
+  auto* expr = create<ast::BitcastExpression>(
+      ty.i32,
       create<ast::BinaryExpression>(
-          Source{}, ast::BinaryOp::kLogicalAnd, a,
-          create<ast::BinaryExpression>(Source{}, ast::BinaryOp::kLogicalOr, b,
-                                        c)));
+          ast::BinaryOp::kLogicalAnd, a,
+          create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, b, c)));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(pre_result(), R"(bool _tint_tmp = a;
 if (_tint_tmp) {
   bool _tint_tmp_0 = b;
@@ -666,48 +459,27 @@
 TEST_F(HlslGeneratorImplTest_Binary, Call_WithLogical) {
   // foo(a && b, c || d, (a || c) && (b || d))
 
-  ast::type::Void void_type;
-
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("foo"), "foo", ast::VariableList{},
-      &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+      mod->RegisterSymbol("foo"), "foo", ast::VariableList{}, ty.void_,
+      create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ast::ExpressionList params;
+  params.push_back(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
+                                                 Expr("a"), Expr("b")));
+  params.push_back(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
+                                                 Expr("c"), Expr("d")));
   params.push_back(create<ast::BinaryExpression>(
-      Source{}, ast::BinaryOp::kLogicalAnd,
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"), "a"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b")));
-  params.push_back(create<ast::BinaryExpression>(
-      Source{}, ast::BinaryOp::kLogicalOr,
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"), "c"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"),
-                                        "d")));
-  params.push_back(create<ast::BinaryExpression>(
-      Source{}, ast::BinaryOp::kLogicalAnd,
-      create<ast::BinaryExpression>(
-          Source{}, ast::BinaryOp::kLogicalOr,
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                            "a"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("c"),
-                                            "c")),
-      create<ast::BinaryExpression>(
-          Source{}, ast::BinaryOp::kLogicalOr,
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("d"),
-                                            "d"))));
+      ast::BinaryOp::kLogicalAnd,
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"),
+                                    Expr("c")),
+      create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("b"),
+                                    Expr("d"))));
 
-  ast::CallStatement expr(Source{},
-                          create<ast::CallExpression>(
-                              Source{},
-                              create<ast::IdentifierExpression>(
-                                  Source{}, mod.RegisterSymbol("foo"), "foo"),
-                              params));
+  auto* expr = create<ast::CallStatement>(Call("foo", params));
 
-  ASSERT_TRUE(gen.EmitStatement(out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, expr)) << gen.error();
   EXPECT_EQ(result(), R"(bool _tint_tmp = a;
 if (_tint_tmp) {
   _tint_tmp = b;
diff --git a/src/writer/hlsl/generator_impl_bitcast_test.cc b/src/writer/hlsl/generator_impl_bitcast_test.cc
index 88181b2..c47df56 100644
--- a/src/writer/hlsl/generator_impl_bitcast_test.cc
+++ b/src/writer/hlsl/generator_impl_bitcast_test.cc
@@ -30,32 +30,26 @@
 using HlslGeneratorImplTest_Bitcast = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Bitcast, EmitExpression_Bitcast_Float) {
-  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(pre, out, &bitcast)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, bitcast)) << gen.error();
   EXPECT_EQ(result(), "asfloat(id)");
 }
 
 TEST_F(HlslGeneratorImplTest_Bitcast, EmitExpression_Bitcast_Int) {
-  ast::type::I32 i32;
-  auto* id = create<ast::IdentifierExpression>(Source{},
-                                               mod.RegisterSymbol("id"), "id");
-  ast::BitcastExpression bitcast(Source{}, &i32, id);
+  auto* id = Expr("id");
+  auto* bitcast = create<ast::BitcastExpression>(ty.i32, id);
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &bitcast)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, bitcast)) << gen.error();
   EXPECT_EQ(result(), "asint(id)");
 }
 
 TEST_F(HlslGeneratorImplTest_Bitcast, EmitExpression_Bitcast_Uint) {
-  ast::type::U32 u32;
-  auto* id = create<ast::IdentifierExpression>(Source{},
-                                               mod.RegisterSymbol("id"), "id");
-  ast::BitcastExpression bitcast(Source{}, &u32, id);
+  auto* id = Expr("id");
+  auto* bitcast = create<ast::BitcastExpression>(ty.u32, id);
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &bitcast)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, bitcast)) << gen.error();
   EXPECT_EQ(result(), "asuint(id)");
 }
 
diff --git a/src/writer/hlsl/generator_impl_block_test.cc b/src/writer/hlsl/generator_impl_block_test.cc
index c7fa1e2..304fe12 100644
--- a/src/writer/hlsl/generator_impl_block_test.cc
+++ b/src/writer/hlsl/generator_impl_block_test.cc
@@ -26,12 +26,12 @@
 using HlslGeneratorImplTest_Block = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Block, 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(out, &b)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, b)) << gen.error();
   EXPECT_EQ(result(), R"(  {
     discard;
   }
@@ -39,12 +39,12 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Block, 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(out, &b)) << gen.error();
+  ASSERT_TRUE(gen.EmitBlock(out, b)) << gen.error();
   EXPECT_EQ(result(), R"({
     discard;
   })");
diff --git a/src/writer/hlsl/generator_impl_break_test.cc b/src/writer/hlsl/generator_impl_break_test.cc
index d5a6e34..54d26b8 100644
--- a/src/writer/hlsl/generator_impl_break_test.cc
+++ b/src/writer/hlsl/generator_impl_break_test.cc
@@ -27,11 +27,11 @@
 using HlslGeneratorImplTest_Break = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Break, Emit_Break) {
-  ast::BreakStatement b(Source{});
+  auto* b = create<ast::BreakStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &b)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, b)) << gen.error();
   EXPECT_EQ(result(), "  break;\n");
 }
 
diff --git a/src/writer/hlsl/generator_impl_call_test.cc b/src/writer/hlsl/generator_impl_call_test.cc
index 225c962..5cb1e0a 100644
--- a/src/writer/hlsl/generator_impl_call_test.cc
+++ b/src/writer/hlsl/generator_impl_call_test.cc
@@ -30,64 +30,41 @@
 using HlslGeneratorImplTest_Call = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Call, EmitExpression_Call_WithoutParams) {
-  ast::type::Void void_type;
-
-  auto* id = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func");
-  ast::CallExpression call(Source{}, id, {});
+  auto* call = Call("my_func");
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
-      &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+      mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{}, ty.void_,
+      create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &call)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
   EXPECT_EQ(result(), "my_func()");
 }
 
 TEST_F(HlslGeneratorImplTest_Call, EmitExpression_Call_WithParams) {
-  ast::type::Void void_type;
-
-  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* call = Call("my_func", "param1", "param2");
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
-      &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+      mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{}, ty.void_,
+      create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &call)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
   EXPECT_EQ(result(), "my_func(param1, param2)");
 }
 
 TEST_F(HlslGeneratorImplTest_Call, EmitStatement_Call) {
-  ast::type::Void void_type;
-
-  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::CallStatement call(Source{},
-                          create<ast::CallExpression>(Source{}, id, params));
+  auto* call = create<ast::CallStatement>(Call("my_func", "param1", "param2"));
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
-      &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+      mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{}, ty.void_,
+      create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
   gen.increment_indent();
-  ASSERT_TRUE(gen.EmitStatement(out, &call)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, call)) << gen.error();
   EXPECT_EQ(result(), "  my_func(param1, param2);\n");
 }
 
diff --git a/src/writer/hlsl/generator_impl_case_test.cc b/src/writer/hlsl/generator_impl_case_test.cc
index 8970ce9..23a9a94 100644
--- a/src/writer/hlsl/generator_impl_case_test.cc
+++ b/src/writer/hlsl/generator_impl_case_test.cc
@@ -31,19 +31,16 @@
 using HlslGeneratorImplTest_Case = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Case, 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(Literal(5));
+  auto* c = create<ast::CaseStatement>(lit, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(out, &c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(out, c)) << gen.error();
   EXPECT_EQ(result(), R"(  case 5: {
     break;
   }
@@ -51,17 +48,14 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Case, Emit_Case_BreaksByDefault) {
-  ast::type::I32 i32;
-
   ast::CaseSelectorList lit;
-  lit.push_back(create<ast::SintLiteral>(Source{}, &i32, 5));
-  ast::CaseStatement c(
-      Source{}, lit,
-      create<ast::BlockStatement>(Source{}, ast::StatementList{}));
+  lit.push_back(Literal(5));
+  auto* c = create<ast::CaseStatement>(
+      lit, create<ast::BlockStatement>(ast::StatementList{}));
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(out, &c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(out, c)) << gen.error();
   EXPECT_EQ(result(), R"(  case 5: {
     break;
   }
@@ -69,19 +63,16 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Case, Emit_Case_WithFallthrough) {
-  ast::type::I32 i32;
-
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::FallthroughStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::FallthroughStatement>(),
+  });
   ast::CaseSelectorList lit;
-  lit.push_back(create<ast::SintLiteral>(Source{}, &i32, 5));
-  ast::CaseStatement c(Source{}, lit, body);
+  lit.push_back(Literal(5));
+  auto* c = create<ast::CaseStatement>(lit, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(out, &c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(out, c)) << gen.error();
   EXPECT_EQ(result(), R"(  case 5: {
     /* fallthrough */
   }
@@ -89,20 +80,17 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Case, 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(Literal(5));
+  lit.push_back(Literal(6));
+  auto* c = create<ast::CaseStatement>(lit, body);
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitCase(out, &c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(out, c)) << gen.error();
   EXPECT_EQ(result(), R"(  case 5:
   case 6: {
     break;
@@ -111,15 +99,14 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Case, 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(out, &c)) << gen.error();
+  ASSERT_TRUE(gen.EmitCase(out, c)) << gen.error();
   EXPECT_EQ(result(), R"(  default: {
     break;
   }
diff --git a/src/writer/hlsl/generator_impl_cast_test.cc b/src/writer/hlsl/generator_impl_cast_test.cc
index 23671ed..6625755 100644
--- a/src/writer/hlsl/generator_impl_cast_test.cc
+++ b/src/writer/hlsl/generator_impl_cast_test.cc
@@ -29,29 +29,14 @@
 using HlslGeneratorImplTest_Cast = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Cast, EmitExpression_Cast_Scalar) {
-  ast::type::F32 f32;
-
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("id"), "id"));
-
-  ast::TypeConstructorExpression cast(Source{}, &f32, params);
-
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &cast)) << gen.error();
+  auto* cast = Construct<f32>("id");
+  ASSERT_TRUE(gen.EmitExpression(pre, out, cast)) << gen.error();
   EXPECT_EQ(result(), "float(id)");
 }
 
 TEST_F(HlslGeneratorImplTest_Cast, EmitExpression_Cast_Vector) {
-  ast::type::F32 f32;
-  ast::type::Vector vec3(&f32, 3);
-
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("id"), "id"));
-
-  ast::TypeConstructorExpression cast(Source{}, &vec3, params);
-
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &cast)) << gen.error();
+  auto* cast = vec3<f32>("id");
+  ASSERT_TRUE(gen.EmitExpression(pre, out, cast)) << gen.error();
   EXPECT_EQ(result(), "float3(id)");
 }
 
diff --git a/src/writer/hlsl/generator_impl_constructor_test.cc b/src/writer/hlsl/generator_impl_constructor_test.cc
index ba46d8e..55512a4 100644
--- a/src/writer/hlsl/generator_impl_constructor_test.cc
+++ b/src/writer/hlsl/generator_impl_constructor_test.cc
@@ -36,154 +36,83 @@
 using HlslGeneratorImplTest_Constructor = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Bool) {
-  ast::type::Bool bool_type;
-  auto* lit = create<ast::BoolLiteral>(Source{}, &bool_type, false);
-  ast::ScalarConstructorExpression expr(Source{}, lit);
+  auto* expr = Expr(false);
 
-  ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "false");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Int) {
-  ast::type::I32 i32;
-  auto* lit = create<ast::SintLiteral>(Source{}, &i32, -12345);
-  ast::ScalarConstructorExpression expr(Source{}, lit);
+  auto* expr = Expr(-12345);
 
-  ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "-12345");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_UInt) {
-  ast::type::U32 u32;
-  auto* lit = create<ast::UintLiteral>(Source{}, &u32, 56779);
-  ast::ScalarConstructorExpression expr(Source{}, lit);
+  auto* expr = Expr(56779u);
 
-  ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "56779u");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, 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);
+  auto* expr = Expr(static_cast<float>((1 << 30) - 4));
 
-  ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "1073741824.0f");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Float) {
-  ast::type::F32 f32;
+  auto* expr = Construct<f32>(-1.2e-5f);
 
-  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(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "float(-0.000012f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Bool) {
-  ast::type::Bool b;
+  auto* expr = Construct<bool>(true);
 
-  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(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "bool(true)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Int) {
-  ast::type::I32 i32;
+  auto* expr = Construct<i32>(-12345);
 
-  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(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "int(-12345)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Uint) {
-  ast::type::U32 u32;
+  auto* expr = Construct<u32>(12345u);
 
-  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(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "uint(12345u)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
+  auto* expr = vec3<f32>(1.f, 2.f, 3.f);
 
-  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(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "float3(1.0f, 2.0f, 3.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
+  auto* expr = vec3<f32>();
 
-  ast::ExpressionList values;
-  ast::TypeConstructorExpression expr(Source{}, &vec, values);
-
-  ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "float3(0.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat) {
-  ast::type::F32 f32;
-  ast::type::Matrix mat(&f32, 3, 2);  // 3 ROWS, 2 COLUMNS
-  ast::type::Vector vec(&f32, 3);
-
   // WGSL matrix is mat2x3 (it flips for AST, sigh). With a type constructor
   // of <vec3, vec3>
 
-  ast::ExpressionList mat_values;
+  auto* expr = mat2x3<f32>(vec3<f32>(1.f, 2.f, 3.f), vec3<f32>(3.f, 4.f, 5.f));
 
-  for (size_t i = 0; i < 2; 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)));
-    auto* lit3 = create<ast::FloatLiteral>(Source{}, &f32,
-                                           static_cast<float>(3 + (i * 2)));
-
-    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));
-
-    mat_values.push_back(
-        create<ast::TypeConstructorExpression>(Source{}, &vec, values));
-  }
-
-  ast::TypeConstructorExpression expr(Source{}, &mat, mat_values);
-
-  ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
 
   // A matrix of type T with n columns and m rows can also be constructed from
   // n vectors of type T with m components.
@@ -192,32 +121,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Array) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
-  ast::type::Array ary(&vec, 3, ast::ArrayDecorationList{});
+  auto* expr = Construct(ty.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));
 
-  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(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitConstructor(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             "{float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f),"
             " float3(7.0f, 8.0f, 9.0f)}");
diff --git a/src/writer/hlsl/generator_impl_continue_test.cc b/src/writer/hlsl/generator_impl_continue_test.cc
index 0502ea6..7e7aec4 100644
--- a/src/writer/hlsl/generator_impl_continue_test.cc
+++ b/src/writer/hlsl/generator_impl_continue_test.cc
@@ -27,11 +27,11 @@
 using HlslGeneratorImplTest_Continue = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Continue, Emit_Continue) {
-  ast::ContinueStatement c(Source{});
+  auto* c = create<ast::ContinueStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &c)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, c)) << gen.error();
   EXPECT_EQ(result(), "  continue;\n");
 }
 
diff --git a/src/writer/hlsl/generator_impl_discard_test.cc b/src/writer/hlsl/generator_impl_discard_test.cc
index 5e7a13c..cd96c69 100644
--- a/src/writer/hlsl/generator_impl_discard_test.cc
+++ b/src/writer/hlsl/generator_impl_discard_test.cc
@@ -24,11 +24,11 @@
 using HlslGeneratorImplTest_Discard = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Discard, Emit_Discard) {
-  ast::DiscardStatement stmt(Source{});
+  auto* stmt = create<ast::DiscardStatement>();
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), "  discard;\n");
 }
 
diff --git a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
index 7a8236b..4c1d4ca 100644
--- a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
+++ b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
@@ -23,10 +23,6 @@
 #include "src/ast/pipeline_stage.h"
 #include "src/ast/return_statement.h"
 #include "src/ast/stage_decoration.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
-#include "src/ast/type/vector_type.h"
-#include "src/ast/type/void_type.h"
 #include "src/ast/variable.h"
 #include "src/type_determiner.h"
 #include "src/writer/hlsl/test_helper.h"
@@ -48,62 +44,34 @@
   //   int bar : TEXCOORD1;
   // };
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
+  auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                   // source
-                            "foo",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &f32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                   // source
-                            "bar",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &i32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kInput, ty.i32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("vtx_main"), "vtx_main", params, &f32, body,
+      mod->RegisterSymbol("vtx_main"), "vtx_main", params, ty.f32, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
+          create<ast::StageDecoration>(ast::PipelineStage::kVertex),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
@@ -127,63 +95,35 @@
   //   int bar : TEXCOORD1;
   // };
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
+  auto* foo_var = Var("foo", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                    // source
-                            "foo",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                    // source
-                            "bar",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &i32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.i32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("vtx_main"), "vtx_main", params, &f32, body,
+      mod->RegisterSymbol("vtx_main"), "vtx_main", params, ty.f32, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
+          create<ast::StageDecoration>(ast::PipelineStage::kVertex),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
@@ -207,63 +147,35 @@
   //   int bar : TEXCOORD1;
   // };
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
+  auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                   // source
-                            "foo",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &f32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                   // source
-                            "bar",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &i32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kInput, ty.i32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      mod->RegisterSymbol("main"), "main", params, ty.f32, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kVertex),
+          create<ast::StageDecoration>(ast::PipelineStage::kVertex),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
@@ -287,62 +199,34 @@
   //   int bar : SV_Target1;
   // };
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
+  auto* foo_var = Var("foo", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                    // source
-                            "foo",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                    // source
-                            "bar",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &i32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.i32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      mod->RegisterSymbol("main"), "main", params, ty.f32, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
@@ -363,62 +247,34 @@
   //
   // -> Error, not allowed
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
+  auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                   // source
-                            "foo",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &f32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                   // source
-                            "bar",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &i32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kInput, ty.i32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      mod->RegisterSymbol("main"), "main", params, ty.f32, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
+          create<ast::StageDecoration>(ast::PipelineStage::kCompute),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
@@ -434,62 +290,34 @@
   //
   // -> Error not allowed
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
+  auto* foo_var = Var("foo", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                    // source
-                            "foo",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                    // source
-                            "bar",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &i32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.i32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar")),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("foo"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("bar")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &f32, body,
+      mod->RegisterSymbol("main"), "main", params, ty.f32, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
+          create<ast::StageDecoration>(ast::PipelineStage::kCompute),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
@@ -511,61 +339,35 @@
   //   float depth : SV_Depth;
   // };
 
-  ast::type::F32 f32;
-  ast::type::Void void_type;
-  ast::type::Vector vec4(&f32, 4);
+  auto* coord_var =
+      Var("coord", ast::StorageClass::kInput, ty.vec4<f32>(), nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragCoord),
+          });
 
-  auto* coord_var = create<ast::Variable>(
-      Source{},                   // source
-      "coord",                    // name
-      ast::StorageClass::kInput,  // storage_class
-      &vec4,                      // type
-      false,                      // is_const
-      nullptr,                    // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragCoord),
-      });
-
-  auto* depth_var = create<ast::Variable>(
-      Source{},                    // source
-      "depth",                     // name
-      ast::StorageClass::kOutput,  // storage_class
-      &f32,                        // type
-      false,                       // is_const
-      nullptr,                     // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragDepth),
-      });
+  auto* depth_var =
+      Var("depth", ast::StorageClass::kOutput, ty.f32, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragDepth),
+          });
 
   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"))),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("depth"), Member("coord", "x")),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &void_type, body,
+      mod->RegisterSymbol("main"), "main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   std::unordered_set<std::string> globals;
 
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index 60499bf..e63723a 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -53,17 +53,14 @@
 using HlslGeneratorImplTest_Function = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function) {
-  ast::type::Void void_type;
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
+  auto* func = create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func",
+                                     ast::VariableList{}, ty.void_, body,
+                                     ast::FunctionDecorationList{});
 
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  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(out)) << gen.error();
@@ -75,17 +72,14 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function_Name_Collision) {
-  ast::type::Void void_type;
-
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("GeometryShader"), "GeometryShader",
-      ast::VariableList{}, &void_type, body, ast::FunctionDecorationList{});
+      mod->RegisterSymbol("GeometryShader"), "GeometryShader",
+      ast::VariableList{}, ty.void_, body, ast::FunctionDecorationList{});
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
   gen.increment_indent();
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -97,38 +91,18 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithParams) {
-  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
+  params.push_back(Var("a", ast::StorageClass::kNone, ty.f32));
+  params.push_back(Var("b", ast::StorageClass::kNone, ty.i32));
 
-  ast::type::Void void_type;
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
+  auto* func =
+      create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func", params,
+                            ty.void_, body, ast::FunctionDecorationList{});
 
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  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(out)) << gen.error();
@@ -141,58 +115,34 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_WithInOutVars) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
+  auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                   // source
-                            "foo",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &f32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                    // source
-                            "bar",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   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>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(
-                        Source{},
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("bar"), "bar"),
-                        create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("foo")),
+      create<ast::ReturnStatement>(),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -215,63 +165,36 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_WithInOut_Builtins) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
+  auto* coord_var =
+      Var("coord", ast::StorageClass::kInput, ty.vec4<f32>(), nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragCoord),
+          });
 
-  auto* coord_var = create<ast::Variable>(
-      Source{},                   // source
-      "coord",                    // name
-      ast::StorageClass::kInput,  // storage_class
-      &vec4,                      // type
-      false,                      // is_const
-      nullptr,                    // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragCoord),
-      });
-
-  auto* depth_var = create<ast::Variable>(
-      Source{},                    // source
-      "depth",                     // name
-      ast::StorageClass::kOutput,  // storage_class
-      &f32,                        // type
-      false,                       // is_const
-      nullptr,                     // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragDepth),
-      });
+  auto* depth_var =
+      Var("depth", ast::StorageClass::kOutput, ty.f32, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragDepth),
+          });
 
   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{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("depth"), Member("coord", "x")),
+      create<ast::ReturnStatement>(),
+  });
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -294,54 +217,31 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_Uniform) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
-
   auto* coord_var =
-      create<ast::Variable>(Source{},                     // source
-                            "coord",                      // name
-                            ast::StorageClass::kUniform,  // storage_class
-                            &vec4,                        // type
-                            false,                        // is_const
-                            nullptr,                      // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::BindingDecoration>(Source{}, 0),
-                                create<ast::SetDecoration>(Source{}, 1),
-                            });
+      Var("coord", ast::StorageClass::kUniform, ty.vec4<f32>(), nullptr,
+          ast::VariableDecorationList{
+              create<ast::BindingDecoration>(0),
+              create<ast::SetDecoration>(1),
+          });
 
   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
-      false,                         // is_const
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("coord"), "coord"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("x"),
-                                            "x")),  // constructor
-      ast::VariableDecorationList{});               // decorations
+  auto* var = Var("v", ast::StorageClass::kFunction, ty.f32,
+                  Member("coord", "x"), 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("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -359,69 +259,42 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_UniformStruct) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "coord", &vec4, ast::StructMemberDecorationList{}));
+      "coord", ty.vec4<f32>(), ast::StructMemberDecorationList{}));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Uniforms"), "Uniforms", str);
+  ast::type::Struct s(mod->RegisterSymbol("Uniforms"), "Uniforms", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                     // source
-                            "uniforms",                   // name
-                            ast::StorageClass::kUniform,  // storage_class
-                            &s,                           // type
-                            false,                        // is_const
-                            nullptr,                      // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::BindingDecoration>(Source{}, 0),
-                                create<ast::SetDecoration>(Source{}, 1),
-                            });
+  auto* coord_var = Var("uniforms", ast::StorageClass::kUniform, &s, nullptr,
+                        ast::VariableDecorationList{
+                            create<ast::BindingDecoration>(0),
+                            create<ast::SetDecoration>(1),
+                        });
 
-  mod.AddConstructedType(&s);
+  mod->AddConstructedType(&s);
 
   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
-      false,                         // is_const
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::MemberAccessorExpression>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("uniforms"), "uniforms"),
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("coord"), "coord")),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("x"),
-                                            "x")),  // constructor
-      ast::VariableDecorationList{});               // decorations
+  auto* var = Var("v", ast::StorageClass::kFunction, ty.f32,
+                  create<ast::MemberAccessorExpression>(
+                      Member("uniforms", "coord"), Expr("x")),
+                  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("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -441,69 +314,45 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer_Read) {
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
 
   auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "coord",                            // 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{}, 1),
-                            });
+      Var("coord", ast::StorageClass::kStorageBuffer, &ac, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BindingDecoration>(0),
+              create<ast::SetDecoration>(1),
+          });
 
   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
-      false,                         // is_const
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("coord"), "coord"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b")),  // constructor
-      ast::VariableDecorationList{});               // decorations
+  auto* var = Var("v", ast::StorageClass::kFunction, ty.f32,
+                  Member("coord", "b"), 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("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -519,69 +368,46 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer_Read) {
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadOnly, &s);
 
   auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "coord",                            // 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{}, 1),
-                            });
+      Var("coord", ast::StorageClass::kStorageBuffer, &ac, nullptr,
+          ast::VariableDecorationList{
+              // decorations
+              create<ast::BindingDecoration>(0),
+              create<ast::SetDecoration>(1),
+          });
 
   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
-      false,                         // is_const
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("coord"), "coord"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b")),  // constructor
-      ast::VariableDecorationList{});               // decorations
+  auto* var = Var("v", ast::StorageClass::kFunction, ty.f32,
+                  Member("coord", "b"), 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("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -597,66 +423,44 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_StorageBuffer_Store) {
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
   ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &s);
 
   auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "coord",                            // 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{}, 1),
-                            });
+      Var("coord", ast::StorageClass::kStorageBuffer, &ac, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BindingDecoration>(0),
+              create<ast::SetDecoration>(1),
+          });
 
   td.RegisterVariableForTesting(coord_var);
 
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
-  auto* body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::MemberAccessorExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("coord"), "coord"),
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("b"), "b")),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.0f))),
-          create<ast::ReturnStatement>(Source{}),
-      });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Member("coord", "b"), Expr(2.0f)),
+      create<ast::ReturnStatement>(),
+  });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main",
-      ast::VariableList{}, &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", ast::VariableList{},
+      ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -673,113 +477,55 @@
 TEST_F(
     HlslGeneratorImplTest_Function,
     Emit_FunctionDecoration_Called_By_EntryPoints_WithLocationGlobals_And_Params) {  // NOLINT
-  ast::type::Void void_type;
-  ast::type::F32 f32;
 
-  auto* foo_var =
-      create<ast::Variable>(Source{},                   // source
-                            "foo",                      // name
-                            ast::StorageClass::kInput,  // storage_class
-                            &f32,                       // type
-                            false,                      // is_const
-                            nullptr,                    // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
+  auto* foo_var = Var("foo", ast::StorageClass::kInput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
-  auto* bar_var =
-      create<ast::Variable>(Source{},                    // source
-                            "bar",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
-  auto* val_var =
-      create<ast::Variable>(Source{},                    // source
-                            "val",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 0),
-                            });
+  auto* val_var = Var("val", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(0),
+                      });
 
   td.RegisterVariableForTesting(foo_var);
   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(
-      create<ast::Variable>(Source{},                         // source
-                            "param",                          // name
-                            ast::StorageClass::kFunction,     // storage_class
-                            &f32,                             // type
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  params.push_back(Var("param", ast::StorageClass::kFunction, ty.f32));
 
-  auto* body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("bar"), "bar"),
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("foo"), "foo")),
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("val"), "val"),
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("param"), "param")),
-          create<ast::ReturnStatement>(
-              Source{}, create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("foo"), "foo")),
-      });
-  auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("bar"), Expr("foo")),
+      create<ast::AssignmentStatement>(Expr("val"), Expr("param")),
+      create<ast::ReturnStatement>(Expr("foo")),
+  });
+  auto* sub_func =
+      create<ast::Function>(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)));
-
-  body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("bar"), "bar"),
-              create<ast::CallExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
-                  expr)),
-          create<ast::ReturnStatement>(Source{}),
-      });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("bar"), Call("sub_func", 1.0f)),
+      create<ast::ReturnStatement>(),
+  });
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      mod->RegisterSymbol("ep_1"), "ep_1", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -809,74 +555,39 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_Called_By_EntryPoints_NoUsedGlobals) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
-
-  auto* depth_var = create<ast::Variable>(
-      Source{},                    // source
-      "depth",                     // name
-      ast::StorageClass::kOutput,  // storage_class
-      &f32,                        // type
-      false,                       // is_const
-      nullptr,                     // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragDepth),
-      });
+  auto* depth_var =
+      Var("depth", ast::StorageClass::kOutput, ty.f32, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragDepth),
+          });
 
   td.RegisterVariableForTesting(depth_var);
 
-  mod.AddGlobalVariable(depth_var);
+  mod->AddGlobalVariable(depth_var);
 
   ast::VariableList params;
-  params.push_back(
-      create<ast::Variable>(Source{},                         // source
-                            "param",                          // name
-                            ast::StorageClass::kFunction,     // storage_class
-                            &f32,                             // type
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  params.push_back(Var("param", ast::StorageClass::kFunction, ty.f32));
 
-  auto* body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::ReturnStatement>(
-              Source{}, create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("param"), "param")),
-      });
-  auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(Expr("param")),
+  });
+  auto* sub_func =
+      create<ast::Function>(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)));
-
-  body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("depth"), "depth"),
-              create<ast::CallExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
-                  expr)),
-          create<ast::ReturnStatement>(Source{}),
-      });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("depth"), Call("sub_func", 1.0f)),
+      create<ast::ReturnStatement>(),
+  });
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      mod->RegisterSymbol("ep_1"), "ep_1", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -900,98 +611,49 @@
 TEST_F(
     HlslGeneratorImplTest_Function,
     Emit_FunctionDecoration_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) {  // NOLINT
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
 
-  auto* coord_var = create<ast::Variable>(
-      Source{},                   // source
-      "coord",                    // name
-      ast::StorageClass::kInput,  // storage_class
-      &vec4,                      // type
-      false,                      // is_const
-      nullptr,                    // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragCoord),
-      });
+  auto* coord_var =
+      Var("coord", ast::StorageClass::kInput, ty.vec4<f32>(), nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragCoord),
+          });
 
-  auto* depth_var = create<ast::Variable>(
-      Source{},                    // source
-      "depth",                     // name
-      ast::StorageClass::kOutput,  // storage_class
-      &f32,                        // type
-      false,                       // is_const
-      nullptr,                     // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::BuiltinDecoration>(Source{}, ast::Builtin::kFragDepth),
-      });
+  auto* depth_var =
+      Var("depth", ast::StorageClass::kOutput, ty.f32, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BuiltinDecoration>(ast::Builtin::kFragDepth),
+          });
 
   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(
-      create<ast::Variable>(Source{},                         // source
-                            "param",                          // name
-                            ast::StorageClass::kFunction,     // storage_class
-                            &f32,                             // type
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  params.push_back(Var("param", ast::StorageClass::kFunction, ty.f32));
 
-  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{}, create<ast::IdentifierExpression>(
-                            Source{}, mod.RegisterSymbol("param"), "param")),
-      });
-  auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("depth"), Member("coord", "x")),
+      create<ast::ReturnStatement>(Expr("param")),
+  });
+  auto* sub_func =
+      create<ast::Function>(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)));
-
-  body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("depth"), "depth"),
-              create<ast::CallExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
-                  expr)),
-          create<ast::ReturnStatement>(Source{}),
-      });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("depth"), Call("sub_func", 1.0f)),
+      create<ast::ReturnStatement>(),
+  });
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      mod->RegisterSymbol("ep_1"), "ep_1", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -1019,86 +681,45 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_Uniform) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
-
   auto* coord_var =
-      create<ast::Variable>(Source{},                     // source
-                            "coord",                      // name
-                            ast::StorageClass::kUniform,  // storage_class
-                            &vec4,                        // type
-                            false,                        // is_const
-                            nullptr,                      // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::BindingDecoration>(Source{}, 0),
-                                create<ast::SetDecoration>(Source{}, 1),
-                            });
+      Var("coord", ast::StorageClass::kUniform, ty.vec4<f32>(), nullptr,
+          ast::VariableDecorationList{
+              create<ast::BindingDecoration>(0),
+              create<ast::SetDecoration>(1),
+          });
 
   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
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  params.push_back(Var("param", ast::StorageClass::kFunction, ty.f32));
 
-  auto* body = create<ast::BlockStatement>(
-      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"))),
-      });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(Member("coord", "x")),
+  });
 
-  auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+  auto* sub_func =
+      create<ast::Function>(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)));
+  auto* var = Var("v", ast::StorageClass::kFunction, ty.f32,
+                  Call("sub_func", 1.0f), ast::VariableDecorationList{});
 
-  auto* var = create<ast::Variable>(
-      Source{},                      // source
-      "v",                           // name
-      ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
-      false,                         // is_const
-      create<ast::CallExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
-          expr),                       // constructor
-      ast::VariableDecorationList{});  // decorations
-
-  body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::VariableDeclStatement>(Source{}, var),
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::VariableDeclStatement>(var),
+      create<ast::ReturnStatement>(),
+  });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -1120,86 +741,46 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_StorageBuffer) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::Vector vec4(&f32, 4);
-  ast::type::AccessControl ac(ast::AccessControl::kReadWrite, &vec4);
+  ast::type::AccessControl ac(ast::AccessControl::kReadWrite, ty.vec4<f32>());
   auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "coord",                            // 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{}, 1),
-                            });
+      Var("coord", ast::StorageClass::kStorageBuffer, &ac, nullptr,
+          ast::VariableDecorationList{
+              create<ast::BindingDecoration>(0),
+              create<ast::SetDecoration>(1),
+          });
 
   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
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  params.push_back(Var("param", ast::StorageClass::kFunction, ty.f32));
 
-  auto* body = create<ast::BlockStatement>(
-      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"))),
-      });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(Member("coord", "x")),
+  });
 
-  auto* sub_func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("sub_func"), "sub_func", params, &f32, body,
-      ast::FunctionDecorationList{});
+  auto* sub_func =
+      create<ast::Function>(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)));
+  auto* var = Var("v", ast::StorageClass::kFunction, ty.f32,
+                  Call("sub_func", 1.0f), ast::VariableDecorationList{});
 
-  auto* var = create<ast::Variable>(
-      Source{},                      // source
-      "v",                           // name
-      ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
-      false,                         // is_const
-      create<ast::CallExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(
-              Source{}, mod.RegisterSymbol("sub_func"), "sub_func"),
-          expr),                       // constructor
-      ast::VariableDecorationList{});  // decorations
-
-  body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::VariableDeclStatement>(Source{}, var),
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::VariableDeclStatement>(var),
+      create<ast::ReturnStatement>(),
+  });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("frag_main"), "frag_main", params,
-      &void_type, body,
+      mod->RegisterSymbol("frag_main"), "frag_main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -1219,59 +800,34 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoints_WithGlobal_Nested_Return) {
-  ast::type::Void void_type;
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-
-  auto* bar_var =
-      create<ast::Variable>(Source{},                    // source
-                            "bar",                       // name
-                            ast::StorageClass::kOutput,  // storage_class
-                            &f32,                        // type
-                            false,                       // is_const
-                            nullptr,                     // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::LocationDecoration>(Source{}, 1),
-                            });
+  auto* bar_var = Var("bar", ast::StorageClass::kOutput, ty.f32, nullptr,
+                      ast::VariableDecorationList{
+                          create<ast::LocationDecoration>(1),
+                      });
 
   td.RegisterVariableForTesting(bar_var);
-  mod.AddGlobalVariable(bar_var);
+  mod->AddGlobalVariable(bar_var);
 
-  auto* list = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* list = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
   ast::VariableList params;
-  auto* body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::AssignmentStatement>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("bar"), "bar"),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.0f))),
-          create<ast::IfStatement>(
-              Source{},
-              create<ast::BinaryExpression>(
-                  Source{}, ast::BinaryOp::kEqual,
-                  create<ast::ScalarConstructorExpression>(
-                      Source{}, create<ast::SintLiteral>(Source{}, &i32, 1)),
-                  create<ast::ScalarConstructorExpression>(
-                      Source{}, create<ast::SintLiteral>(Source{}, &i32, 1))),
-              list, ast::ElseStatementList{}),
-          create<ast::ReturnStatement>(Source{}),
-      });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(Expr("bar"), Expr(1.0f)),
+      create<ast::IfStatement>(create<ast::BinaryExpression>(
+                                   ast::BinaryOp::kEqual, Expr(1), Expr(1)),
+                               list, ast::ElseStatementList{}),
+      create<ast::ReturnStatement>(),
+  });
 
   auto* func_1 = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("ep_1"), "ep_1", params, &void_type, body,
+      mod->RegisterSymbol("ep_1"), "ep_1", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func_1);
+  mod->AddFunction(func_1);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -1293,17 +849,15 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_WithNameCollision) {
-  ast::type::Void void_type;
-
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("GeometryShader"), "GeometryShader",
-      ast::VariableList{}, &void_type,
-      create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+      mod->RegisterSymbol("GeometryShader"), "GeometryShader",
+      ast::VariableList{}, ty.void_,
+      create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kFragment),
+          create<ast::StageDecoration>(ast::PipelineStage::kFragment),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
   EXPECT_EQ(result(), R"(void GeometryShader_tint_0() {
@@ -1314,21 +868,18 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_Compute) {
-  ast::type::Void void_type;
-
   ast::VariableList params;
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &void_type, body,
+      mod->RegisterSymbol("main"), "main", 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(out)) << gen.error();
@@ -1342,22 +893,19 @@
 
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_Compute_WithWorkgroup) {
-  ast::type::Void void_type;
-
   ast::VariableList params;
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("main"), "main", params, &void_type, body,
+      mod->RegisterSymbol("main"), "main", params, ty.void_, body,
       ast::FunctionDecorationList{
-          create<ast::StageDecoration>(Source{}, ast::PipelineStage::kCompute),
-          create<ast::WorkgroupDecoration>(Source{}, 2u, 4u, 6u),
+          create<ast::StageDecoration>(ast::PipelineStage::kCompute),
+          create<ast::WorkgroupDecoration>(2u, 4u, 6u),
       });
 
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
@@ -1370,30 +918,19 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Function_WithArrayParams) {
-  ast::type::F32 f32;
-  ast::type::Array ary(&f32, 5, ast::ArrayDecorationList{});
+  ast::type::Array ary(ty.f32, 5, ast::ArrayDecorationList{});
 
   ast::VariableList params;
-  params.push_back(
-      create<ast::Variable>(Source{},                         // source
-                            "a",                              // name
-                            ast::StorageClass::kNone,         // storage_class
-                            &ary,                             // type
-                            false,                            // is_const
-                            nullptr,                          // constructor
-                            ast::VariableDecorationList{}));  // decorations
+  params.push_back(Var("a", ast::StorageClass::kNone, &ary));
 
-  ast::type::Void void_type;
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
+  auto* func =
+      create<ast::Function>(mod->RegisterSymbol("my_func"), "my_func", params,
+                            ty.void_, body, ast::FunctionDecorationList{});
 
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  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(out)) << gen.error();
@@ -1422,101 +959,65 @@
   //   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{
+                           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,
+                    Member("data", "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,
+                    Member("data", "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();
diff --git a/src/writer/hlsl/generator_impl_identifier_test.cc b/src/writer/hlsl/generator_impl_identifier_test.cc
index 52fd804..eba1c18 100644
--- a/src/writer/hlsl/generator_impl_identifier_test.cc
+++ b/src/writer/hlsl/generator_impl_identifier_test.cc
@@ -24,16 +24,15 @@
 using HlslGeneratorImplTest_Identifier = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Identifier, EmitIdentifierExpression) {
-  ast::IdentifierExpression i(Source{}, mod.RegisterSymbol("foo"), "foo");
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &i)) << gen.error();
+  auto* i = Expr("foo");
+  ASSERT_TRUE(gen.EmitExpression(pre, out, i)) << gen.error();
   EXPECT_EQ(result(), "foo");
 }
 
 TEST_F(HlslGeneratorImplTest_Identifier,
        EmitIdentifierExpression_Single_WithCollision) {
-  ast::IdentifierExpression i(Source{}, mod.RegisterSymbol("virtual"),
-                              "virtual");
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &i)) << gen.error();
+  auto* i = Expr("virtual");
+  ASSERT_TRUE(gen.EmitExpression(pre, out, i)) << gen.error();
   EXPECT_EQ(result(), "virtual_tint_0");
 }
 
diff --git a/src/writer/hlsl/generator_impl_if_test.cc b/src/writer/hlsl/generator_impl_if_test.cc
index 0b1105c..3825a10 100644
--- a/src/writer/hlsl/generator_impl_if_test.cc
+++ b/src/writer/hlsl/generator_impl_if_test.cc
@@ -27,16 +27,14 @@
 using HlslGeneratorImplTest_If = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_If, Emit_If) {
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  ast::IfStatement i(Source{}, cond, body, ast::ElseStatementList{});
+  auto* cond = Expr("cond");
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(cond, body, ast::ElseStatementList{});
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, i)) << gen.error();
   EXPECT_EQ(result(), R"(  if (cond) {
     return;
   }
@@ -44,26 +42,22 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, 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::ReturnStatement>(Source{}),
-                });
+  auto* else_cond = Expr("else_cond");
+  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(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::ReturnStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(
+      cond, body,
+      ast::ElseStatementList{create<ast::ElseStatement>(else_cond, else_body)});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, i)) << gen.error();
   EXPECT_EQ(result(), R"(  if (cond) {
     return;
   } else {
@@ -75,24 +69,21 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElse) {
-  auto* else_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(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::ReturnStatement>(),
+  });
+  auto* i = create<ast::IfStatement>(
+      cond, body,
+      ast::ElseStatementList{create<ast::ElseStatement>(nullptr, else_body)});
 
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, i)) << gen.error();
   EXPECT_EQ(result(), R"(  if (cond) {
     return;
   } else {
@@ -102,35 +93,30 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithMultiple) {
-  auto* else_cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("else_cond"), "else_cond");
+  auto* else_cond = Expr("else_cond");
 
-  auto* else_body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* else_body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
-  auto* else_body_2 = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
+  auto* else_body_2 = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
 
-  auto* cond = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("cond"), "cond");
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(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::ReturnStatement>(),
+  });
+  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(out, &i)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, i)) << gen.error();
   EXPECT_EQ(result(), R"(  if (cond) {
     return;
   } else {
diff --git a/src/writer/hlsl/generator_impl_import_test.cc b/src/writer/hlsl/generator_impl_import_test.cc
index 882c4d7..04a7785 100644
--- a/src/writer/hlsl/generator_impl_import_test.cc
+++ b/src/writer/hlsl/generator_impl_import_test.cc
@@ -50,18 +50,11 @@
 TEST_P(HlslImportData_SingleParamTest, FloatScalar) {
   auto param = GetParam();
 
-  ast::type::F32 f32;
+  auto* ident = Expr(param.name);
+  auto* expr = Call(ident, 1.f);
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-
-  auto* ident = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol(param.name), param.name);
-  ast::CallExpression expr(Source{}, ident, params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -96,20 +89,10 @@
 TEST_P(HlslImportData_SingleIntParamTest, IntScalar) {
   auto param = GetParam();
 
-  ast::type::I32 i32;
+  auto* expr = Call(param.name, Expr(1));
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 1)));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
-      params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -120,22 +103,10 @@
 TEST_P(HlslImportData_DualParamTest, FloatScalar) {
   auto param = GetParam();
 
-  ast::type::F32 f32;
+  auto* expr = Call(param.name, 1.f, 2.f);
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.f)));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
-      params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.0f, 2.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -152,40 +123,11 @@
 TEST_P(HlslImportData_DualParam_VectorTest, FloatVector) {
   auto param = GetParam();
 
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
+  auto* expr =
+      Call(param.name, vec3<f32>(1.f, 2.f, 3.f), vec3<f32>(4.f, 5.f, 6.f));
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::TypeConstructorExpression>(
-      Source{}, &vec,
-      ast::ExpressionList{
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.f)),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.f)),
-      }));
-
-  params.push_back(create<ast::TypeConstructorExpression>(
-      Source{}, &vec,
-      ast::ExpressionList{
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 4.f)),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 5.f)),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::FloatLiteral>(Source{}, &f32, 6.f)),
-      }));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
-      params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             std::string(param.hlsl_name) +
                 "(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))");
@@ -198,22 +140,10 @@
 TEST_P(HlslImportData_DualParam_Int_Test, IntScalar) {
   auto param = GetParam();
 
-  ast::type::I32 i32;
+  auto* expr = Call(param.name, 1, 2);
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 1)));
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 2)));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
-      params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1, 2)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -225,24 +155,10 @@
 TEST_P(HlslImportData_TripleParamTest, FloatScalar) {
   auto param = GetParam();
 
-  ast::type::F32 f32;
+  auto* expr = Call(param.name, 1.f, 2.f, 3.f);
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.f)));
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.f)));
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.f)));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
-      params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.0f, 2.0f, 3.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -261,24 +177,10 @@
 TEST_P(HlslImportData_TripleParam_Int_Test, IntScalar) {
   auto param = GetParam();
 
-  ast::type::I32 i32;
+  auto* expr = Call(param.name, 1, 2, 3);
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 1)));
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 2)));
-  params.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 3)));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol(param.name), param.name),
-      params);
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1, 2, 3)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
@@ -286,34 +188,16 @@
                          testing::Values(HlslImportData{"clamp", "clamp"}));
 
 TEST_F(HlslGeneratorImplTest_Import, HlslImportData_Determinant) {
-  ast::type::F32 f32;
-  ast::type::Matrix mat(&f32, 3, 3);
+  auto* var = Var("var", ast::StorageClass::kFunction, ty.mat3x3<f32>());
 
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "var",                           // name
-                            ast::StorageClass::kFunction,    // storage_class
-                            &mat,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* expr = Call("determinant", "var");
 
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("var"), "var"));
-
-  ast::CallExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol("determinant"), "determinant"),
-      params);
-
-  mod.AddGlobalVariable(var);
+  mod->AddGlobalVariable(var);
 
   // Register the global
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitCall(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), std::string("determinant(var)"));
 }
 
diff --git a/src/writer/hlsl/generator_impl_intrinsic_test.cc b/src/writer/hlsl/generator_impl_intrinsic_test.cc
index ca670fc..1841ef6 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_test.cc
@@ -71,50 +71,22 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Intrinsic, DISABLED_Intrinsic_OuterProduct) {
-  ast::type::F32 f32;
-  ast::type::Vector vec2(&f32, 2);
-  ast::type::Vector vec3(&f32, 3);
+  auto* a = Var("a", ast::StorageClass::kNone, ty.vec2<f32>());
+  auto* b = Var("b", ast::StorageClass::kNone, ty.vec3<f32>());
 
-  auto* a =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &vec2,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-  auto* b =
-      create<ast::Variable>(Source{},                        // source
-                            "b",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &vec3,                           // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-
-  ast::ExpressionList params;
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("a"), "a"));
-  params.push_back(create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("b"), "b"));
-
-  ast::CallExpression call(
-      Source{},
-      create<ast::IdentifierExpression>(
-          Source{}, mod.RegisterSymbol("outer_product"), "outer_product"),
-      params);
+  auto* call = Call("outer_product", "a", "b");
 
   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();
+  ASSERT_TRUE(td.DetermineResultType(call)) << td.error();
 
   gen.increment_indent();
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &call)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
   EXPECT_EQ(result(), "  float3x2(a * b[0], a * b[1], a * b[2])");
 }
 
@@ -123,32 +95,18 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Intrinsic, Intrinsic_Call) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
+  auto* call = Call("dot", "param1", "param2");
 
-  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* v1 = Var("param1", ast::StorageClass::kFunction, ty.vec3<f32>());
+  auto* v2 = Var("param2", ast::StorageClass::kFunction, ty.vec3<f32>());
 
-  ast::CallExpression call(Source{},
-                           create<ast::IdentifierExpression>(
-                               Source{}, mod.RegisterSymbol("dot"), "dot"),
-                           params);
+  td.RegisterVariableForTesting(v1);
+  td.RegisterVariableForTesting(v2);
 
-  ast::Variable v1(Source{}, "param1", ast::StorageClass::kFunction, &vec,
-                   false, nullptr, ast::VariableDecorationList{});
-  ast::Variable v2(Source{}, "param2", ast::StorageClass::kFunction, &vec,
-                   false, nullptr, ast::VariableDecorationList{});
-
-  td.RegisterVariableForTesting(&v1);
-  td.RegisterVariableForTesting(&v2);
-
-  ASSERT_TRUE(td.DetermineResultType(&call)) << td.error();
+  ASSERT_TRUE(td.DetermineResultType(call)) << td.error();
 
   gen.increment_indent();
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &call)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, call)) << gen.error();
   EXPECT_EQ(result(), "  dot(param1, param2)");
 }
 
diff --git a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
index a48c82c..eeaef41 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
@@ -272,8 +272,7 @@
   param.buildTextureVariable(this);
   param.buildSamplerVariable(this);
 
-  auto* call =
-      create<ast::CallExpression>(Expr(param.function), param.args(this));
+  auto* call = Call(param.function, param.args(this));
 
   ASSERT_TRUE(td.Determine()) << td.error();
   ASSERT_TRUE(td.DetermineResultType(call)) << td.error();
diff --git a/src/writer/hlsl/generator_impl_loop_test.cc b/src/writer/hlsl/generator_impl_loop_test.cc
index bbd3a4e..4e7f017 100644
--- a/src/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/writer/hlsl/generator_impl_loop_test.cc
@@ -34,14 +34,13 @@
 using HlslGeneratorImplTest_Loop = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Loop, 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(out, &l)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, l)) << gen.error();
   EXPECT_EQ(result(), R"(  for(;;) {
     discard;
   }
@@ -49,18 +48,16 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopWithContinuing) {
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  auto* continuing = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(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::ReturnStatement>(),
+  });
+  auto* l = create<ast::LoopStatement>(body, continuing);
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &l)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, l)) << gen.error();
   EXPECT_EQ(result(), R"(  {
     bool tint_hlsl_is_first_1 = true;
     for(;;) {
@@ -76,36 +73,29 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
-  ast::type::F32 f32;
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::DiscardStatement>(),
+  });
+  auto* continuing = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::ReturnStatement>(),
+  });
+  auto* inner = create<ast::LoopStatement>(body, continuing);
 
-  auto* body = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::DiscardStatement>(Source{}),
-                });
-  auto* continuing = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::ReturnStatement>(Source{}),
-                });
-  auto* inner = create<ast::LoopStatement>(Source{}, body, continuing);
+  body = create<ast::BlockStatement>(ast::StatementList{
+      inner,
+  });
 
-  body = create<ast::BlockStatement>(Source{}, ast::StatementList{
-                                                   inner,
-                                               });
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
 
-  auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("lhs"), "lhs");
-  auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("rhs"), "rhs");
+  continuing = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(lhs, rhs),
+  });
 
-  continuing = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(Source{}, lhs, rhs),
-                });
-
-  ast::LoopStatement outer(Source{}, body, continuing);
+  auto* outer = create<ast::LoopStatement>(body, continuing);
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &outer)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, outer)) << gen.error();
   EXPECT_EQ(result(), R"(  {
     bool tint_hlsl_is_first_1 = true;
     for(;;) {
@@ -152,47 +142,25 @@
   //   }
   // }
 
-  ast::type::F32 f32;
+  auto* var = Var("lhs", ast::StorageClass::kFunction, ty.f32, Expr(2.4f),
+                  ast::VariableDecorationList{});
 
-  auto* var = create<ast::Variable>(
-      Source{},                      // source
-      "lhs",                         // name
-      ast::StorageClass::kFunction,  // storage_class
-      &f32,                          // type
-      false,                         // is_const
-      create<ast::ScalarConstructorExpression>(
-          Source{},
-          create<ast::FloatLiteral>(Source{}, &f32, 2.4)),  // constructor
-      ast::VariableDecorationList{});                       // decorations
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::VariableDeclStatement>(var),
+      create<ast::VariableDeclStatement>(
+          Var("other", ast::StorageClass::kFunction, ty.f32)),
+  });
 
-  auto* body = create<ast::BlockStatement>(
-      Source{},
-      ast::StatementList{
-          create<ast::VariableDeclStatement>(Source{}, var),
-          create<ast::VariableDeclStatement>(
-              Source{}, create<ast::Variable>(
-                            Source{},                      // source
-                            "other",                       // name
-                            ast::StorageClass::kFunction,  // storage_class
-                            &f32,                          // type
-                            false,                         // is_const
-                            nullptr,                       // constructor
-                            ast::VariableDecorationList{})),
-      });
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
 
-  auto* lhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("lhs"), "lhs");
-  auto* rhs = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("rhs"), "rhs");
-
-  auto* continuing = create<ast::BlockStatement>(
-      Source{}, ast::StatementList{
-                    create<ast::AssignmentStatement>(Source{}, lhs, rhs),
-                });
-  ast::LoopStatement outer(Source{}, body, continuing);
+  auto* continuing = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(lhs, rhs),
+  });
+  auto* outer = create<ast::LoopStatement>(body, continuing);
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &outer)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, outer)) << gen.error();
   EXPECT_EQ(result(), R"(  {
     bool tint_hlsl_is_first_1 = true;
     float lhs;
diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc
index b8d2a8e..5c484f1 100644
--- a/src/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -16,8 +16,6 @@
 
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/assignment_statement.h"
-#include "src/ast/binary_expression.h"
-#include "src/ast/float_literal.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/member_accessor_expression.h"
 #include "src/ast/module.h"
@@ -27,10 +25,6 @@
 #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/f32_type.h"
-#include "src/ast/type/i32_type.h"
-#include "src/ast/type/matrix_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/vector_type.h"
 #include "src/ast/type_constructor_expression.h"
@@ -46,40 +40,25 @@
 using HlslGeneratorImplTest_MemberAccessor = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   ast::StructMemberDecorationList deco;
-  deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "mem", &f32, deco));
+  deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("mem", ty.f32, deco));
 
-  auto* strct =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* strct = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Str"), "Str", strct);
+  ast::type::Struct s(mod->RegisterSymbol("Str"), "Str", strct);
 
-  auto* str_var =
-      create<ast::Variable>(Source{},                        // source
-                            "str",                           // name
-                            ast::StorageClass::kPrivate,     // storage_class
-                            &s,                              // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* str_var = Var("str", ast::StorageClass::kPrivate, &s);
 
-  auto* str = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("str"), "str");
-  auto* mem = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("mem"), "mem");
-
-  ast::MemberAccessorExpression expr(Source{}, str, mem);
+  auto* expr = Member("str", "mem");
 
   td.RegisterVariableForTesting(str_var);
   gen.register_global(str_var);
-  mod.AddGlobalVariable(str_var);
+  mod->AddGlobalVariable(str_var);
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "str.mem");
 }
 
@@ -93,47 +72,32 @@
   // data.b;
   //
   // -> asfloat(data.Load(4));
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b"));
+  auto* expr = Member("data", "b");
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr));
+  ASSERT_TRUE(td.DetermineResultType(expr));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load(4))");
 }
 
@@ -147,47 +111,32 @@
   // data.a;
   //
   // -> asint(data.Load(0));
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
+  auto* expr = Member("data", "a");
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr));
+  ASSERT_TRUE(td.DetermineResultType(expr));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asint(data.Load(0))");
 }
 TEST_F(HlslGeneratorImplTest_MemberAccessor,
@@ -203,66 +152,42 @@
   // -> float3x2 _tint_tmp = b;
   //    data.Store3(4 + 0, asuint(_tint_tmp[0]));
   //    data.Store3(4 + 16, asuint(_tint_tmp[1]));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Matrix mat(&f32, 3, 2);
 
   auto* str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "z", &i32,
+              "z", ty.i32,
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "a", &mat,
+              "a", ty.mat2x3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 4)}),
+                  create<ast::StructMemberOffsetDecoration>(4)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* b_var =
-      create<ast::Variable>(Source{},                        // source
-                            "b",                             // name
-                            ast::StorageClass::kPrivate,     // storage_class
-                            &mat,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* b_var = Var("b", ast::StorageClass::kPrivate, ty.mat2x3<f32>());
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
-  auto* rhs =
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"), "b");
+  auto* lhs = Member("data", "a");
+  auto* rhs = Expr("b");
 
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
   td.RegisterVariableForTesting(coord_var);
   td.RegisterVariableForTesting(b_var);
   gen.register_global(coord_var);
   gen.register_global(b_var);
-  mod.AddGlobalVariable(coord_var);
-  mod.AddGlobalVariable(b_var);
+  mod->AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(b_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&assign));
+  ASSERT_TRUE(td.DetermineResultType(assign));
 
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(), R"(float3x2 _tint_tmp = b;
 data.Store3(4 + 0, asuint(_tint_tmp[0]));
 data.Store3(4 + 16, asuint(_tint_tmp[1]));
@@ -282,52 +207,35 @@
   // 0.0f, 0.0f, 0.0f);
   //    data.Store3(4 + 0, asuint(_tint_tmp[0]);
   //    data.Store3(4 + 16, asuint(_tint_tmp[1]));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Matrix mat(&f32, 3, 2);
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "z", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("z", ty.i32, a_deco));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &mat, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("a", ty.mat2x3<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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
-  auto* rhs = create<ast::TypeConstructorExpression>(Source{}, &mat,
-                                                     ast::ExpressionList{});
+  auto* lhs = Member("data", "a");
+  auto* rhs = Construct(ty.mat2x3<f32>(), ast::ExpressionList{});
 
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&assign));
+  ASSERT_TRUE(td.DetermineResultType(assign));
 
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(
       result(),
       R"(float3x2 _tint_tmp = float3x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
@@ -347,48 +255,32 @@
   //
   // -> asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8),
   // data.Load2(4 + 16)));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Matrix mat(&f32, 2, 3);
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "z", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("z", ty.i32, a_deco));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &mat, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("a", ty.mat3x2<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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
+  auto* expr = Member("data", "a");
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr));
+  ASSERT_TRUE(td.DetermineResultType(expr));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             "asfloat(uint2x3(data.Load2(4 + 0), data.Load2(4 + 8), "
             "data.Load2(4 + 16)))");
@@ -408,48 +300,32 @@
   // data.b.a;
   //
   // -> asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Matrix mat(&f32, 3, 2);
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "z", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("z", ty.i32, a_deco));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &mat, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(4));
+  members.push_back(create<ast::StructMember>("a", ty.mat2x3<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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
+  auto* expr = Member("data", "a");
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr));
+  ASSERT_TRUE(td.DetermineResultType(expr));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             "asfloat(uint3x2(data.Load3(4 + 0), data.Load3(4 + 16)))");
 }
@@ -465,44 +341,28 @@
   //
   // -> asfloat(uint3x3(data.Load3(0), data.Load3(16),
   // data.Load3(32)));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Matrix mat(&f32, 3, 3);
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList deco;
-  deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &mat, deco));
+  deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.mat3x3<f32>(), deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
+  auto* expr = Member("data", "a");
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr));
+  ASSERT_TRUE(td.DetermineResultType(expr));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(),
             "asfloat(uint3x3(data.Load3(0 + 0), data.Load3(0 + 16), "
             "data.Load3(0 + 32)))");
@@ -518,56 +378,32 @@
   // data.a[2][1];
   //
   // -> asfloat(data.Load((2 * 16) + (1 * 4) + 16)))
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Matrix mat(&f32, 3, 4);
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "z", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("z", ty.i32, a_deco));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 16));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &mat, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16));
+  members.push_back(create<ast::StructMember>("a", ty.mat4x3<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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::ArrayAccessorExpression expr(
-      Source{},
-      create<ast::ArrayAccessorExpression>(
-          Source{},
-          create<ast::MemberAccessorExpression>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("data"), "data"),
-              create<ast::IdentifierExpression>(Source{},
-                                                mod.RegisterSymbol("a"), "a")),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-      create<ast::ScalarConstructorExpression>(
-          Source{}, create<ast::SintLiteral>(Source{}, &i32, 1)));
+  auto* expr = Index(Index(Member("data", "a"), Expr(2)), Expr(1));
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr));
+  ASSERT_TRUE(td.DetermineResultType(expr));
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load((4 * 1) + (16 * 2) + 16))");
 }
 
@@ -580,51 +416,32 @@
   // data.a[2];
   //
   // -> asint(data.Load((2 * 4));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Array ary(&i32, 5,
+  ast::type::Array ary(ty.i32, 5,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 4),
+                           create<ast::StrideDecoration>(4),
                        });
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &ary, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", &ary, a_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::ArrayAccessorExpression expr(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(Source{},
-                                            mod.RegisterSymbol("data"), "data"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                            "a")),
-      create<ast::ScalarConstructorExpression>(
-          Source{}, create<ast::SintLiteral>(Source{}, &i32, 2)));
+  auto* expr = Index(Member("data", "a"), Expr(2));
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asint(data.Load((4 * 2) + 0))");
 }
 
@@ -637,59 +454,32 @@
   // data.a[(2 + 4) - 3];
   //
   // -> asint(data.Load((4 * ((2 + 4) - 3)));
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Array ary(&i32, 5,
+  ast::type::Array ary(ty.i32, 5,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 4),
+                           create<ast::StrideDecoration>(4),
                        });
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &ary, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", &ary, a_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
-  ast::ArrayAccessorExpression expr(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(Source{},
-                                            mod.RegisterSymbol("data"), "data"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                            "a")),
-      create<ast::BinaryExpression>(
-          Source{}, ast::BinaryOp::kSubtract,
-          create<ast::BinaryExpression>(
-              Source{}, ast::BinaryOp::kAdd,
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::SintLiteral>(Source{}, &i32, 2)),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::SintLiteral>(Source{}, &i32, 4))),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::SintLiteral>(Source{}, &i32, 3))));
+  auto* expr = Index(Member("data", "a"), Sub(Add(Expr(2), Expr(4)), Expr(3)));
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  ASSERT_TRUE(td.DetermineResultType(expr)) << td.error();
 
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asint(data.Load((4 * ((2 + 4) - 3)) + 0))");
 }
 
@@ -704,50 +494,33 @@
   //
   // -> data.Store(0, asuint(2.0f));
 
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b"));
-  auto* rhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.0f));
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
+  auto* lhs = Member("data", "b");
+  auto* rhs = Expr(2.0f);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&assign));
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(assign));
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(), R"(data.Store(4, asuint(2.0f));
 )");
 }
@@ -762,54 +535,34 @@
   //
   // -> data.Store((2 * 4), asuint(2.3f));
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Array ary(&i32, 5,
+  ast::type::Array ary(ty.i32, 5,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 4),
+                           create<ast::StrideDecoration>(4),
                        });
 
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &ary, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", &ary, a_deco));
 
-  auto* str =
-      create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* str = create<ast::Struct>(members, ast::StructDecorationList{});
 
-  ast::type::Struct s(mod.RegisterSymbol("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  auto* lhs = create<ast::ArrayAccessorExpression>(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::IdentifierExpression>(Source{},
-                                            mod.RegisterSymbol("data"), "data"),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                            "a")),
-      create<ast::ScalarConstructorExpression>(
-          Source{}, create<ast::SintLiteral>(Source{}, &i32, 2)));
-  auto* rhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
+  auto* lhs = Index(Member("data", "a"), Expr(2));
+  auto* rhs = Expr(2);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&assign)) << td.error();
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(assign)) << td.error();
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(), R"(data.Store((4 * 2) + 0, asuint(2));
 )");
 }
@@ -825,50 +578,33 @@
   //
   // -> data.Store(0, asuint(2));
 
-  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{}, "a", &i32, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.i32, a_deco));
 
   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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("a"),
-                                        "a"));
-  auto* rhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::SintLiteral>(Source{}, &i32, 2));
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
+  auto* lhs = Member("data", "a");
+  auto* rhs = Expr(2);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
-  ASSERT_TRUE(td.DetermineResultType(&assign));
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(assign));
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(), R"(data.Store(0, asuint(2));
 )");
 }
@@ -884,49 +620,31 @@
   //
   // -> asfloat(data.Load(16));
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &ivec3, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.vec3<i32>(), a_deco));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 16));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &fvec3, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16));
+  members.push_back(create<ast::StructMember>("b", ty.vec3<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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b"));
+  auto* expr = Member("data", "b");
 
-  ASSERT_TRUE(td.DetermineResultType(&expr));
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr));
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load3(16))");
 }
 
@@ -941,60 +659,34 @@
   //
   // -> data.Store(16, asuint(float3(2.3f, 1.2f, 0.2f)));
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   ast::StructMemberList members;
   ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 0));
-  members.push_back(create<ast::StructMember>(Source{}, "a", &ivec3, a_deco));
+  a_deco.push_back(create<ast::StructMemberOffsetDecoration>(0));
+  members.push_back(create<ast::StructMember>("a", ty.vec3<i32>(), a_deco));
 
   ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 16));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &fvec3, b_deco));
+  b_deco.push_back(create<ast::StructMemberOffsetDecoration>(16));
+  members.push_back(create<ast::StructMember>("b", ty.vec3<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("Data"), "Data", str);
+  ast::type::Struct s(mod->RegisterSymbol("Data"), "Data", str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &s,                                 // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &s);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  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));
+  auto* lhs = Member("data", "b");
+  auto* rhs = vec3<f32>(1.f, 2.f, 3.f);
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("data"),
-                                        "data"),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b"));
-  auto* rhs = create<ast::TypeConstructorExpression>(Source{}, &fvec3, values);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
-
-  ASSERT_TRUE(td.DetermineResultType(&assign));
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(assign));
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(),
             R"(data.Store3(16, asuint(float3(1.0f, 2.0f, 3.0f)));
 )");
@@ -1015,76 +707,49 @@
   //
   // -> asfloat(data.Load3(16 + (2 * 32)))
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   auto* data_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "a", &ivec3,
+              "a", ty.vec3<i32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "b", &fvec3,
+              "b", ty.vec3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 16)}),
+                  create<ast::StructMemberOffsetDecoration>(16)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct data(mod.RegisterSymbol("Data"), "Data", data_str);
+  ast::type::Struct data(mod->RegisterSymbol("Data"), "Data", data_str);
 
   ast::type::Array ary(&data, 4,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 32),
+                           create<ast::StrideDecoration>(32),
                        });
 
   auto* pre_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "c", &ary,
+              "c", &ary,
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct pre_struct(mod.RegisterSymbol("Pre"), "Pre", pre_str);
+  ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &pre_struct,                        // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &pre_struct);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::ArrayAccessorExpression>(
-          Source{},
-          create<ast::MemberAccessorExpression>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("data"), "data"),
-              create<ast::IdentifierExpression>(Source{},
-                                                mod.RegisterSymbol("c"), "c")),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b"));
+  auto* expr = Member(Index(Member("data", "c"), Expr(2)), "b");
 
-  ASSERT_TRUE(td.DetermineResultType(&expr));
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr));
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load3(16 + (32 * 2) + 0))");
 }
 
@@ -1103,80 +768,48 @@
   //
   // -> asfloat(data.Load3(16 + (2 * 32))).xy
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   ast::StructMemberList members;
   ast::StructMemberDecorationList deco;
 
   auto* data_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "a", &ivec3,
+              "a", ty.vec3<i32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "b", &fvec3,
+              "b", ty.vec3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 16)}),
+                  create<ast::StructMemberOffsetDecoration>(16)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct data(mod.RegisterSymbol("Data"), "Data", data_str);
+  ast::type::Struct data(mod->RegisterSymbol("Data"), "Data", data_str);
 
   ast::type::Array ary(
-      &data, 4,
-      ast::ArrayDecorationList{create<ast::StrideDecoration>(Source{}, 32)});
+      &data, 4, ast::ArrayDecorationList{create<ast::StrideDecoration>(32)});
 
   auto* pre_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{create<ast::StructMember>(
-          Source{}, "c", &ary,
+          "c", &ary,
           ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(Source{}, 0)})},
+              create<ast::StructMemberOffsetDecoration>(0)})},
       ast::StructDecorationList{});
 
-  ast::type::Struct pre_struct(mod.RegisterSymbol("Pre"), "Pre", pre_str);
+  ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &pre_struct,                        // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &pre_struct);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::ArrayAccessorExpression>(
-              Source{},
-              create<ast::MemberAccessorExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("data"), "data"),
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("c"), "c")),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b")),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("xy"),
-                                        "xy"));
+  auto* expr = Member(Member(Index(Member("data", "c"), Expr(2)), "b"), "xy");
 
-  ASSERT_TRUE(td.DetermineResultType(&expr));
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr));
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load3(16 + (32 * 2) + 0)).xy");
 }
 
@@ -1196,78 +829,47 @@
   //
   // -> asfloat(data.Load((4 * 1) + 16 + (2 * 32) + 0))
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   auto* data_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "a", &ivec3,
+              "a", ty.vec3<i32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "b", &fvec3,
+              "b", ty.vec3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 16)}),
+                  create<ast::StructMemberOffsetDecoration>(16)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct data(mod.RegisterSymbol("Data"), "Data", data_str);
+  ast::type::Struct data(mod->RegisterSymbol("Data"), "Data", data_str);
 
   ast::type::Array ary(&data, 4,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 32),
+                           create<ast::StrideDecoration>(32),
                        });
 
   auto* pre_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{create<ast::StructMember>(
-          Source{}, "c", &ary,
+          "c", &ary,
           ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(Source{}, 0)})},
+              create<ast::StructMemberOffsetDecoration>(0)})},
       ast::StructDecorationList{});
 
-  ast::type::Struct pre_struct(mod.RegisterSymbol("Pre"), "Pre", pre_str);
+  ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &pre_struct,                        // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &pre_struct);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  ast::MemberAccessorExpression expr(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::ArrayAccessorExpression>(
-              Source{},
-              create<ast::MemberAccessorExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("data"), "data"),
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("c"), "c")),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b")),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("g"),
-                                        "g"));
+  auto* expr = Member(Member(Index(Member("data", "c"), Expr(2)), "b"), "g");
 
-  ASSERT_TRUE(td.DetermineResultType(&expr));
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr));
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load((4 * 1) + 16 + (32 * 2) + 0))");
 }
 
@@ -1286,78 +888,47 @@
   //
   // -> asfloat(data.Load(4 + 16 + (2 * 32)))
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   auto* data_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "a", &ivec3,
+              "a", ty.vec3<i32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "b", &fvec3,
+              "b", ty.vec3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 16)}),
+                  create<ast::StructMemberOffsetDecoration>(16)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct data(mod.RegisterSymbol("Data"), "Data", data_str);
+  ast::type::Struct data(mod->RegisterSymbol("Data"), "Data", data_str);
 
   ast::type::Array ary(&data, 4,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 32),
+                           create<ast::StrideDecoration>(32),
                        });
 
   auto* pre_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{create<ast::StructMember>(
-          Source{}, "c", &ary,
+          "c", &ary,
           ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(Source{}, 0)})},
+              create<ast::StructMemberOffsetDecoration>(0)})},
       ast::StructDecorationList{});
 
-  ast::type::Struct pre_struct(mod.RegisterSymbol("Pre"), "Pre", pre_str);
+  ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &pre_struct,                        // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &pre_struct);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  ast::ArrayAccessorExpression expr(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::ArrayAccessorExpression>(
-              Source{},
-              create<ast::MemberAccessorExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("data"), "data"),
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("c"), "c")),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b")),
-      create<ast::ScalarConstructorExpression>(
-          Source{}, create<ast::SintLiteral>(Source{}, &i32, 1)));
+  auto* expr = Index(Member(Index(Member("data", "c"), Expr(2)), "b"), Expr(1));
 
-  ASSERT_TRUE(td.DetermineResultType(&expr));
-  ASSERT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(expr));
+  ASSERT_TRUE(gen.EmitExpression(pre, out, expr)) << gen.error();
   EXPECT_EQ(result(), "asfloat(data.Load((4 * 1) + 16 + (32 * 2) + 0))");
 }
 
@@ -1376,86 +947,50 @@
   //
   // -> data.Store3(16 + (2 * 32), asuint(float3(1.0f, 2.0f, 3.0f)));
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   auto* data_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "a", &ivec3,
+              "a", ty.vec3<i32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "b", &fvec3,
+              "b", ty.vec3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 16)}),
+                  create<ast::StructMemberOffsetDecoration>(16)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct data(mod.RegisterSymbol("Data"), "Data", data_str);
+  ast::type::Struct data(mod->RegisterSymbol("Data"), "Data", data_str);
 
   ast::type::Array ary(&data, 4,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 32),
+                           create<ast::StrideDecoration>(32),
                        });
 
   auto* pre_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{create<ast::StructMember>(
-          Source{}, "c", &ary,
+          "c", &ary,
           ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(Source{}, 0)})},
+              create<ast::StructMemberOffsetDecoration>(0)})},
       ast::StructDecorationList{});
 
-  ast::type::Struct pre_struct(mod.RegisterSymbol("Pre"), "Pre", pre_str);
+  ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &pre_struct,                        // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &pre_struct);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::ArrayAccessorExpression>(
-          Source{},
-          create<ast::MemberAccessorExpression>(
-              Source{},
-              create<ast::IdentifierExpression>(
-                  Source{}, mod.RegisterSymbol("data"), "data"),
-              create<ast::IdentifierExpression>(Source{},
-                                                mod.RegisterSymbol("c"), "c")),
-          create<ast::ScalarConstructorExpression>(
-              Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                        "b"));
+  auto* lhs = Member(Index(Member("data", "c"), Expr(2)), "b");
 
-  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));
+  auto* assign =
+      create<ast::AssignmentStatement>(lhs, vec3<f32>(1.f, 2.f, 3.f));
 
-  auto* rhs = create<ast::TypeConstructorExpression>(Source{}, &fvec3, values);
-
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
-
-  ASSERT_TRUE(td.DetermineResultType(&assign));
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(assign));
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(),
             R"(data.Store3(16 + (32 * 2) + 0, asuint(float3(1.0f, 2.0f, 3.0f)));
 )");
@@ -1476,83 +1011,50 @@
   //
   // -> data.Store((4 * 1) + 16 + (2 * 32) + 0, asuint(1.0f));
 
-  ast::type::F32 f32;
-  ast::type::I32 i32;
-  ast::type::Vector ivec3(&i32, 3);
-  ast::type::Vector fvec3(&f32, 3);
-
   auto* data_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{
           create<ast::StructMember>(
-              Source{}, "a", &ivec3,
+              "a", ty.vec3<i32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 0)}),
+                  create<ast::StructMemberOffsetDecoration>(0)}),
           create<ast::StructMember>(
-              Source{}, "b", &fvec3,
+              "b", ty.vec3<f32>(),
               ast::StructMemberDecorationList{
-                  create<ast::StructMemberOffsetDecoration>(Source{}, 16)}),
+                  create<ast::StructMemberOffsetDecoration>(16)}),
       },
       ast::StructDecorationList{});
 
-  ast::type::Struct data(mod.RegisterSymbol("Data"), "Data", data_str);
+  ast::type::Struct data(mod->RegisterSymbol("Data"), "Data", data_str);
 
   ast::type::Array ary(&data, 4,
                        ast::ArrayDecorationList{
-                           create<ast::StrideDecoration>(Source{}, 32),
+                           create<ast::StrideDecoration>(32),
                        });
 
   auto* pre_str = create<ast::Struct>(
-      Source{},
       ast::StructMemberList{create<ast::StructMember>(
-          Source{}, "c", &ary,
+          "c", &ary,
           ast::StructMemberDecorationList{
-              create<ast::StructMemberOffsetDecoration>(Source{}, 0)})},
+              create<ast::StructMemberOffsetDecoration>(0)})},
       ast::StructDecorationList{});
 
-  ast::type::Struct pre_struct(mod.RegisterSymbol("Pre"), "Pre", pre_str);
+  ast::type::Struct pre_struct(mod->RegisterSymbol("Pre"), "Pre", pre_str);
 
-  auto* coord_var =
-      create<ast::Variable>(Source{},                           // source
-                            "data",                             // name
-                            ast::StorageClass::kStorageBuffer,  // storage_class
-                            &pre_struct,                        // type
-                            false,                              // is_const
-                            nullptr,                            // constructor
-                            ast::VariableDecorationList{});     // decorations
+  auto* coord_var = Var("data", ast::StorageClass::kStorageBuffer, &pre_struct);
 
   td.RegisterVariableForTesting(coord_var);
   gen.register_global(coord_var);
-  mod.AddGlobalVariable(coord_var);
+  mod->AddGlobalVariable(coord_var);
 
   ASSERT_TRUE(td.Determine()) << td.error();
 
-  auto* lhs = create<ast::MemberAccessorExpression>(
-      Source{},
-      create<ast::MemberAccessorExpression>(
-          Source{},
-          create<ast::ArrayAccessorExpression>(
-              Source{},
-              create<ast::MemberAccessorExpression>(
-                  Source{},
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("data"), "data"),
-                  create<ast::IdentifierExpression>(
-                      Source{}, mod.RegisterSymbol("c"), "c")),
-              create<ast::ScalarConstructorExpression>(
-                  Source{}, create<ast::SintLiteral>(Source{}, &i32, 2))),
-          create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("b"),
-                                            "b")),
-      create<ast::IdentifierExpression>(Source{}, mod.RegisterSymbol("y"),
-                                        "y"));
+  auto* lhs = Member(Member(Index(Member("data", "c"), Expr(2)), "b"), "y");
+  auto* rhs = Expr(1.f);
 
-  auto* rhs = create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &i32, 1.f));
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
 
-  ast::AssignmentStatement assign(Source{}, lhs, rhs);
-
-  ASSERT_TRUE(td.DetermineResultType(&assign));
-  ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
+  ASSERT_TRUE(td.DetermineResultType(assign));
+  ASSERT_TRUE(gen.EmitStatement(out, assign)) << gen.error();
   EXPECT_EQ(result(),
             R"(data.Store((4 * 1) + 16 + (32 * 2) + 0, asuint(1.0f));
 )");
diff --git a/src/writer/hlsl/generator_impl_module_constant_test.cc b/src/writer/hlsl/generator_impl_module_constant_test.cc
index 401c6df..171942f 100644
--- a/src/writer/hlsl/generator_impl_module_constant_test.cc
+++ b/src/writer/hlsl/generator_impl_module_constant_test.cc
@@ -33,47 +33,19 @@
 using HlslGeneratorImplTest_ModuleConstant = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_ModuleConstant) {
-  ast::type::F32 f32;
-  ast::type::Array ary(&f32, 3, ast::ArrayDecorationList{});
-
-  ast::ExpressionList exprs;
-  exprs.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 1.0f)));
-  exprs.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 2.0f)));
-  exprs.push_back(create<ast::ScalarConstructorExpression>(
-      Source{}, create<ast::FloatLiteral>(Source{}, &f32, 3.0f)));
-
   auto* var =
-      create<ast::Variable>(Source{},                  // source
-                            "pos",                     // name
-                            ast::StorageClass::kNone,  // storage_class
-                            &ary,                      // type
-                            true,                      // is_const
-                            create<ast::TypeConstructorExpression>(
-                                Source{}, &ary, exprs),      // constructor
-                            ast::VariableDecorationList{});  // decorations
+      Const("pos", ast::StorageClass::kNone, ty.array<f32, 3>(),
+            array<f32, 3>(1.f, 2.f, 3.f), ast::VariableDecorationList{});
 
   ASSERT_TRUE(gen.EmitProgramConstVariable(out, var)) << gen.error();
   EXPECT_EQ(result(), "static const float pos[3] = {1.0f, 2.0f, 3.0f};\n");
 }
 
 TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant) {
-  ast::type::F32 f32;
-
-  auto* var = create<ast::Variable>(
-      Source{},                  // source
-      "pos",                     // name
-      ast::StorageClass::kNone,  // storage_class
-      &f32,                      // type
-      true,                      // is_const
-      create<ast::ScalarConstructorExpression>(
-          Source{},
-          create<ast::FloatLiteral>(Source{}, &f32, 3.0f)),  // constructor
-      ast::VariableDecorationList{
-          // decorations
-          create<ast::ConstantIdDecoration>(Source{}, 23),
-      });
+  auto* var = Const("pos", ast::StorageClass::kNone, ty.f32, Expr(3.0f),
+                    ast::VariableDecorationList{
+                        create<ast::ConstantIdDecoration>(23),
+                    });
 
   ASSERT_TRUE(gen.EmitProgramConstVariable(out, var)) << gen.error();
   EXPECT_EQ(result(), R"(#ifndef WGSL_SPEC_CONSTANT_23
@@ -85,19 +57,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoConstructor) {
-  ast::type::F32 f32;
-
-  auto* var =
-      create<ast::Variable>(Source{},                  // source
-                            "pos",                     // name
-                            ast::StorageClass::kNone,  // storage_class
-                            &f32,                      // type
-                            true,                      // is_const
-                            nullptr,                   // constructor
-                            ast::VariableDecorationList{
-                                // decorations
-                                create<ast::ConstantIdDecoration>(Source{}, 23),
-                            });
+  auto* var = Const("pos", ast::StorageClass::kNone, ty.f32, nullptr,
+                    ast::VariableDecorationList{
+                        create<ast::ConstantIdDecoration>(23),
+                    });
 
   ASSERT_TRUE(gen.EmitProgramConstVariable(out, var)) << gen.error();
   EXPECT_EQ(result(), R"(#ifndef WGSL_SPEC_CONSTANT_23
diff --git a/src/writer/hlsl/generator_impl_return_test.cc b/src/writer/hlsl/generator_impl_return_test.cc
index a13ef1b..92939e4 100644
--- a/src/writer/hlsl/generator_impl_return_test.cc
+++ b/src/writer/hlsl/generator_impl_return_test.cc
@@ -28,20 +28,18 @@
 using HlslGeneratorImplTest_Return = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Return, Emit_Return) {
-  ast::ReturnStatement r(Source{});
+  auto* r = create<ast::ReturnStatement>();
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &r)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, r)) << gen.error();
   EXPECT_EQ(result(), "  return;\n");
 }
 
 TEST_F(HlslGeneratorImplTest_Return, Emit_ReturnWithValue) {
-  auto* expr = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("expr"), "expr");
-  ast::ReturnStatement r(Source{}, expr);
+  auto* r = create<ast::ReturnStatement>(Expr("expr"));
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &r)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, r)) << gen.error();
   EXPECT_EQ(result(), "  return expr;\n");
 }
 
diff --git a/src/writer/hlsl/generator_impl_switch_test.cc b/src/writer/hlsl/generator_impl_switch_test.cc
index 6bbec6a..9a73bce 100644
--- a/src/writer/hlsl/generator_impl_switch_test.cc
+++ b/src/writer/hlsl/generator_impl_switch_test.cc
@@ -31,34 +31,29 @@
 using HlslGeneratorImplTest_Switch = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Switch, 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(Literal(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(out, &s)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, s)) << gen.error();
   EXPECT_EQ(result(), R"(  switch(cond) {
     case 5: {
       break;
diff --git a/src/writer/hlsl/generator_impl_test.cc b/src/writer/hlsl/generator_impl_test.cc
index db93f62..0d1baa6 100644
--- a/src/writer/hlsl/generator_impl_test.cc
+++ b/src/writer/hlsl/generator_impl_test.cc
@@ -30,10 +30,10 @@
 TEST_F(HlslGeneratorImplTest, Generate) {
   ast::type::Void void_type;
   auto* func = create<ast::Function>(
-      Source{}, mod.RegisterSymbol("my_func"), "my_func", ast::VariableList{},
-      &void_type, create<ast::BlockStatement>(Source{}, ast::StatementList{}),
+      mod->RegisterSymbol("my_func"), "my_func", ast::VariableList{},
+      &void_type, create<ast::BlockStatement>(ast::StatementList{}),
       ast::FunctionDecorationList{});
-  mod.AddFunction(func);
+  mod->AddFunction(func);
 
   ASSERT_TRUE(gen.Generate(out)) << gen.error();
   EXPECT_EQ(result(), R"(void my_func() {
@@ -57,9 +57,7 @@
 TEST_F(HlslGeneratorImplTest, NameConflictWith_InputStructName) {
   ASSERT_EQ(gen.generate_name("func_main_in"), "func_main_in");
 
-  ast::IdentifierExpression ident(Source{}, mod.RegisterSymbol("func_main_in"),
-                                  "func_main_in");
-  ASSERT_TRUE(gen.EmitIdentifier(pre, out, &ident));
+  ASSERT_TRUE(gen.EmitIdentifier(pre, out, Expr("func_main_in")));
   EXPECT_EQ(result(), "func_main_in_0");
 }
 
diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc
index b59c9c4..408226f 100644
--- a/src/writer/hlsl/generator_impl_type_test.cc
+++ b/src/writer/hlsl/generator_impl_type_test.cc
@@ -44,147 +44,105 @@
 using HlslGeneratorImplTest_Type = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Type, 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(out, &alias, "")) << gen.error();
   EXPECT_EQ(result(), "alias");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Alias_NameCollision) {
-  ast::type::F32 f32;
-  ast::type::Alias alias(mod.RegisterSymbol("bool"), "bool", &f32);
+  ast::type::Alias alias(mod->RegisterSymbol("bool"), "bool", ty.f32);
 
   ASSERT_TRUE(gen.EmitType(out, &alias, "")) << gen.error();
   EXPECT_EQ(result(), "bool_tint_0");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &a, "ary")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.array<bool, 4>(), "ary")) << gen.error();
   EXPECT_EQ(result(), "bool ary[4]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-  ast::type::Array c(&a, 5, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &c, "ary")) << gen.error();
+  auto* arr = ty.array(ty.array<bool, 4>(), 5);
+  ASSERT_TRUE(gen.EmitType(out, arr, "ary")) << gen.error();
   EXPECT_EQ(result(), "bool ary[5][4]");
 }
 
 // TODO(dsinclair): Is this possible? What order should it output in?
 TEST_F(HlslGeneratorImplTest_Type,
        DISABLED_EmitType_ArrayOfArrayOfRuntimeArray) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-  ast::type::Array c(&a, 5, ast::ArrayDecorationList{});
-  ast::type::Array d(&c, 0, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &c, "ary")) << gen.error();
+  auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5), 0);
+  ASSERT_TRUE(gen.EmitType(out, arr, "ary")) << gen.error();
   EXPECT_EQ(result(), "bool ary[5][4][1]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-  ast::type::Array c(&a, 5, ast::ArrayDecorationList{});
-  ast::type::Array d(&c, 6, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &d, "ary")) << gen.error();
+  auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5), 6);
+  ASSERT_TRUE(gen.EmitType(out, arr, "ary")) << gen.error();
   EXPECT_EQ(result(), "bool ary[6][5][4]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array_NameCollision) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &a, "bool")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.array<bool, 4>(), "bool")) << gen.error();
   EXPECT_EQ(result(), "bool bool_tint_0[4]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array_WithoutName) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 4, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &a, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.array<bool, 4>(), "")) << gen.error();
   EXPECT_EQ(result(), "bool[4]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_RuntimeArray) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 0, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &a, "ary")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.array<bool>(), "ary")) << gen.error();
   EXPECT_EQ(result(), "bool ary[]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type,
        DISABLED_EmitType_RuntimeArray_NameCollision) {
-  ast::type::Bool b;
-  ast::type::Array a(&b, 0, ast::ArrayDecorationList{});
-
-  ASSERT_TRUE(gen.EmitType(out, &a, "double")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.array<bool>(), "double")) << gen.error();
   EXPECT_EQ(result(), "bool double_tint_0[]");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Bool) {
-  ast::type::Bool b;
-
-  ASSERT_TRUE(gen.EmitType(out, &b, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.bool_, "")) << gen.error();
   EXPECT_EQ(result(), "bool");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_F32) {
-  ast::type::F32 f32;
-
-  ASSERT_TRUE(gen.EmitType(out, &f32, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.f32, "")) << gen.error();
   EXPECT_EQ(result(), "float");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_I32) {
-  ast::type::I32 i32;
-
-  ASSERT_TRUE(gen.EmitType(out, &i32, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.i32, "")) << gen.error();
   EXPECT_EQ(result(), "int");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Matrix) {
-  ast::type::F32 f32;
-  ast::type::Matrix m(&f32, 3, 2);
-
-  ASSERT_TRUE(gen.EmitType(out, &m, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.mat2x3<f32>(), "")) << gen.error();
   EXPECT_EQ(result(), "float3x2");
 }
 
 // TODO(dsinclair): How to annotate as workgroup?
 TEST_F(HlslGeneratorImplTest_Type, DISABLED_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(out, &p, "")) << gen.error();
   EXPECT_EQ(result(), "float*");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, 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(out, &s, "S")) << gen.error();
   EXPECT_EQ(result(), R"(struct S {
@@ -195,46 +153,38 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, 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(out, &s, "")) << gen.error();
   EXPECT_EQ(result(), "S");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberDecorationList decos;
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 4));
+  decos.push_back(create<ast::StructMemberOffsetDecoration>(4));
 
   ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>(Source{}, "a", &i32, decos));
+  members.push_back(create<ast::StructMember>("a", ty.i32, decos));
 
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 32));
-  members.push_back(create<ast::StructMember>(Source{}, "b", &f32, decos));
+  decos.push_back(create<ast::StructMemberOffsetDecoration>(32));
+  members.push_back(create<ast::StructMember>("b", ty.f32, decos));
 
-  decos.push_back(create<ast::StructMemberOffsetDecoration>(Source{}, 128));
-  members.push_back(create<ast::StructMember>(Source{}, "c", &f32, decos));
+  decos.push_back(create<ast::StructMemberOffsetDecoration>(128));
+  members.push_back(create<ast::StructMember>("c", ty.f32, decos));
 
-  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(out, &s, "")) << gen.error();
   EXPECT_EQ(result(), R"(struct {
@@ -248,20 +198,16 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
-  ast::type::I32 i32;
-  ast::type::F32 f32;
-
   ast::StructMemberList members;
   members.push_back(create<ast::StructMember>(
-      Source{}, "double", &i32, ast::StructMemberDecorationList{}));
+      "double", ty.i32, ast::StructMemberDecorationList{}));
 
   ast::StructMemberDecorationList b_deco;
-  members.push_back(create<ast::StructMember>(Source{}, "float", &f32, b_deco));
+  members.push_back(create<ast::StructMember>("float", 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(out, &s, "S")) << gen.error();
   EXPECT_EQ(result(), R"(struct S {
@@ -273,23 +219,20 @@
 
 // TODO(dsinclair): How to translate [[block]]
 TEST_F(HlslGeneratorImplTest_Type, DISABLED_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(out, &s, "B")) << gen.error();
   EXPECT_EQ(result(), R"(struct B {
@@ -299,24 +242,17 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_U32) {
-  ast::type::U32 u32;
-
-  ASSERT_TRUE(gen.EmitType(out, &u32, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.u32, "")) << gen.error();
   EXPECT_EQ(result(), "uint");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Vector) {
-  ast::type::F32 f32;
-  ast::type::Vector v(&f32, 3);
-
-  ASSERT_TRUE(gen.EmitType(out, &v, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.vec3<f32>(), "")) << gen.error();
   EXPECT_EQ(result(), "float3");
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Void) {
-  ast::type::Void v;
-
-  ASSERT_TRUE(gen.EmitType(out, &v, "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(out, ty.void_, "")) << gen.error();
   EXPECT_EQ(result(), "void");
 }
 
@@ -374,8 +310,7 @@
 TEST_P(HlslSampledtexturesTest, Emit) {
   auto params = GetParam();
 
-  ast::type::F32 f32;
-  ast::type::SampledTexture s(params.dim, &f32);
+  ast::type::SampledTexture s(params.dim, ty.f32);
 
   ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.error();
   EXPECT_EQ(result(), params.result);
@@ -396,8 +331,7 @@
                         "TextureCubeArray"}));
 
 TEST_F(HlslGeneratorImplTest_Type, EmitMultisampledTexture) {
-  ast::type::F32 f32;
-  ast::type::MultisampledTexture s(ast::type::TextureDimension::k2d, &f32);
+  ast::type::MultisampledTexture s(ast::type::TextureDimension::k2d, ty.f32);
 
   ASSERT_TRUE(gen.EmitType(out, &s, "")) << gen.error();
   EXPECT_EQ(result(), "Texture2D");
diff --git a/src/writer/hlsl/generator_impl_unary_op_test.cc b/src/writer/hlsl/generator_impl_unary_op_test.cc
index 6ab97a5..77fdf7a 100644
--- a/src/writer/hlsl/generator_impl_unary_op_test.cc
+++ b/src/writer/hlsl/generator_impl_unary_op_test.cc
@@ -37,11 +37,10 @@
 TEST_P(HlslUnaryOpTest, 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(pre, out, &op)) << gen.error();
+  ASSERT_TRUE(gen.EmitExpression(pre, out, op)) << gen.error();
   EXPECT_EQ(result(), std::string(params.name) + "(expr)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_UnaryOp,
diff --git a/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index b0a21e9..5d5eae9 100644
--- a/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -33,163 +33,85 @@
 using HlslGeneratorImplTest_VariableDecl = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, 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(out, &stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), "  float a;\n");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
-  ast::type::F32 f32;
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &f32,                            // type
-                            true,                            // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
+  auto* var = Const("a", ast::StorageClass::kNone, ty.f32);
 
-  ast::VariableDeclStatement stmt(Source{}, var);
+  auto* stmt = create<ast::VariableDeclStatement>(var);
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), "  const float a;\n");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
-  ast::type::F32 f32;
-  ast::type::Array ary(&f32, 5, ast::ArrayDecorationList{});
+  auto* var = Var("a", ast::StorageClass::kNone, ty.array<f32, 5>());
 
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &ary,                            // type
-                            false,                           // is_const
-                            nullptr,                         // constructor
-                            ast::VariableDecorationList{});  // decorations
-
-  ast::VariableDeclStatement stmt(Source{}, var);
+  auto* stmt = create<ast::VariableDeclStatement>(var);
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), "  float a[5];\n");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl,
        Emit_VariableDeclStatement_Function) {
-  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
+  auto* var = Var("a", ast::StorageClass::kFunction, ty.f32);
 
-  ast::VariableDeclStatement stmt(Source{}, var);
+  auto* stmt = create<ast::VariableDeclStatement>(var);
   gen.increment_indent();
 
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), "  float a;\n");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, 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(out, &stmt)) << gen.error();
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), "  float a;\n");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl,
        Emit_VariableDeclStatement_Initializer_Private) {
-  auto* ident = create<ast::IdentifierExpression>(
-      Source{}, mod.RegisterSymbol("initializer"), "initializer");
+  auto* var = Var("a", ast::StorageClass::kNone, ty.f32, Expr("initializer"),
+                  ast::VariableDecorationList{});
 
-  ast::type::F32 f32;
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &f32,                            // type
-                            false,                           // is_const
-                            ident,                           // constructor
-                            ast::VariableDecorationList{});  // decorations
-
-  ast::VariableDeclStatement stmt(Source{}, var);
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  auto* stmt = create<ast::VariableDeclStatement>(var);
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), R"(float a = initializer;
 )");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl,
        Emit_VariableDeclStatement_Initializer_ZeroVec) {
-  ast::type::F32 f32;
-  ast::type::Vector vec(&f32, 3);
+  auto* var = Var("a", ast::StorageClass::kNone, ty.vec3<f32>(), vec3<f32>(),
+                  ast::VariableDecorationList{});
 
-  ast::ExpressionList values;
-  auto* zero_vec =
-      create<ast::TypeConstructorExpression>(Source{}, &vec, values);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &vec,                            // type
-                            false,                           // is_const
-                            zero_vec,                        // constructor
-                            ast::VariableDecorationList{});  // decorations
-
-  ast::VariableDeclStatement stmt(Source{}, var);
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  auto* stmt = create<ast::VariableDeclStatement>(var);
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(), R"(float3 a = float3(0.0f);
 )");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl,
        Emit_VariableDeclStatement_Initializer_ZeroMat) {
-  ast::type::F32 f32;
-  ast::type::Matrix mat(&f32, 3, 2);
+  auto* var = Var("a", ast::StorageClass::kNone, ty.mat2x3<f32>(),
+                  mat2x3<f32>(), ast::VariableDecorationList{});
 
-  ast::ExpressionList values;
-  auto* zero_mat =
-      create<ast::TypeConstructorExpression>(Source{}, &mat, values);
-
-  auto* var =
-      create<ast::Variable>(Source{},                        // source
-                            "a",                             // name
-                            ast::StorageClass::kNone,        // storage_class
-                            &mat,                            // type
-                            false,                           // is_const
-                            zero_mat,                        // constructor
-                            ast::VariableDecorationList{});  // decorations
-
-  ast::VariableDeclStatement stmt(Source{}, var);
-  ASSERT_TRUE(gen.EmitStatement(out, &stmt)) << gen.error();
+  auto* stmt = create<ast::VariableDeclStatement>(var);
+  ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
   EXPECT_EQ(result(),
             R"(float3x2 a = float3x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
 )");
diff --git a/src/writer/hlsl/test_helper.h b/src/writer/hlsl/test_helper.h
index 7f5386c..fd874f4 100644
--- a/src/writer/hlsl/test_helper.h
+++ b/src/writer/hlsl/test_helper.h
@@ -21,7 +21,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/hlsl/generator_impl.h"
 
@@ -31,9 +31,9 @@
 
 /// Helper class for testing
 template <typename BODY>
-class TestHelperBase : public BODY {
+class TestHelperBase : public BODY, public ast::BuilderWithModule {
  public:
-  TestHelperBase() : td(&mod), gen(&mod) {}
+  TestHelperBase() : td(mod), gen(mod) {}
   ~TestHelperBase() = default;
 
   /// @returns the result string
@@ -42,17 +42,6 @@
   /// @returns the pre result string
   std::string pre_result() const { return pre.str(); }
 
-  /// 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>
-  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