ast::Builder: Make the interface more fluent

Add C++ aliases for the wgsl types `i32`, `u32` and `f32`.

Separate types out from the builder and into a `Builder::Types` class. An instance of this is now held by the `Builder::ty` field. Makes it clear when you are referencing a `ast::type` instead of a constructor method.

Rework a number of builder methods so they take the type as a template argument instead of a parameter. This more closely resembles wgsl (example: `vec2<i32>(1,2)`)

Use PascalCase for the constructor methods, but keep the wgsl-like constructors lowercase to imitate the language style.

Add `BuilderWithContext` so that `Builder` can be truely immutable, and so we can remove `set_context()`.

Change-Id: Idf2d7d5abe7d11e27671b8e80d3d56d6bc4b3ca2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32980
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/ast/builder.cc b/src/ast/builder.cc
index c30672a..de6b3ae 100644
--- a/src/ast/builder.cc
+++ b/src/ast/builder.cc
@@ -17,16 +17,28 @@
 namespace tint {
 namespace ast {
 
-Builder::Builder() = default;
+TypesBuilder::TypesBuilder(TypeManager* tm)
+    : bool_(tm->Get<ast::type::BoolType>()),
+      f32(tm->Get<ast::type::F32Type>()),
+      i32(tm->Get<ast::type::I32Type>()),
+      u32(tm->Get<ast::type::U32Type>()),
+      void_(tm->Get<ast::type::VoidType>()),
+      tm_(tm) {}
 
-Builder::Builder(tint::Context* ctx) : ctx_(ctx) {}
-
+Builder::Builder(tint::Context* c) : ctx(c), ty(&c->type_mgr()) {}
 Builder::~Builder() = default;
 
-ast::Variable* Builder::make_var(const std::string& name,
-                                 ast::StorageClass storage,
-                                 ast::type::Type* type) {
-  return create<ast::Variable>(name, storage, type);
+ast::Variable* Builder::Var(const std::string& name,
+                            ast::StorageClass storage,
+                            ast::type::Type* type) {
+  auto* var = create<ast::Variable>(name, storage, type);
+  OnVariableBuilt(var);
+  return var;
+}
+
+BuilderWithContext::BuilderWithContext() : Builder(new Context()) {}
+BuilderWithContext::~BuilderWithContext() {
+  delete ctx;
 }
 
 }  // namespace ast
diff --git a/src/ast/builder.h b/src/ast/builder.h
index 389d6c6..39ae9d2 100644
--- a/src/ast/builder.h
+++ b/src/ast/builder.h
@@ -15,8 +15,6 @@
 #ifndef SRC_AST_BUILDER_H_
 #define SRC_AST_BUILDER_H_
 
-#include <assert.h>
-
 #include <memory>
 #include <string>
 #include <utility>
@@ -42,175 +40,234 @@
 namespace tint {
 namespace ast {
 
-/// Helper for building common AST constructs
-class Builder {
+/// TypesBuilder holds basic `ast::tint` types and methods for constructing
+/// complex types.
+class TypesBuilder {
  public:
   /// Constructor
-  /// Note, the context _must_ be set with |set_context| before the builder
-  /// is used.
-  Builder();
+  /// @param tm the type manager
+  explicit TypesBuilder(TypeManager* tm);
+
+  /// A boolean type
+  ast::type::BoolType* const bool_;
+  /// A f32 type
+  ast::type::F32Type* const f32;
+  /// A i32 type
+  ast::type::I32Type* const i32;
+  /// A u32 type
+  ast::type::U32Type* const u32;
+  /// A void type
+  ast::type::VoidType* const void_;
+
+  /// @return the tint AST type for the C type `T`.
+  template <typename T>
+  ast::type::Type* Of() const;
+
+  /// @return the tint AST type for a 2-element vector of the C type `T`.
+  template <typename T>
+  ast::type::Type* vec2() const;
+
+  /// @return the tint AST type for a 3-element vector of the C type `T`.
+  template <typename T>
+  ast::type::Type* vec3() const;
+
+  /// @return the tint AST type for a 4-element vector of the C type `T`.
+  template <typename T>
+  ast::type::Type* vec4() const;
+
+  /// @return the tint AST type for a 3x3 matrix of the C type `T`.
+  template <typename T>
+  ast::type::Type* mat3x3() const;
+
+  /// @return the tint AST type for an array of type `T`.
+  template <typename T>
+  ast::type::Type* arr() const;
+
+ private:
+  /// CToAST<T> is specialized for various `T` types and each specialization
+  /// contains a single static `get()` method for obtaining the corresponding
+  /// AST type for the C type `T`.
+  /// `get()` has the signature:
+  ///    `static ast::type::Type* get(Types* t)`
+  template <typename T>
+  struct CToAST {};
+
+  TypeManager* const tm_;
+};
+
+/// Helper for building common AST constructs.
+class Builder {
+ public:
+  /// `i32` is a type alias to `int`.
+  /// Useful for passing to template methods such as `vec2<i32>()` to imitate
+  /// WGSL syntax.
+  /// Note: this is intentionally not aliased to uint32_t as we want integer
+  /// literals passed to the builder to match WGSL's integer literal types.
+  using i32 = decltype(1);
+  /// `u32` is a type alias to `unsigned int`.
+  /// Useful for passing to template methods such as `vec2<u32>()` to imitate
+  /// WGSL syntax.
+  /// Note: this is intentionally not aliased to uint32_t as we want integer
+  /// literals passed to the builder to match WGSL's integer literal types.
+  using u32 = decltype(1u);
+  /// `f32` is a type alias to `float`
+  /// Useful for passing to template methods such as `vec2<f32>()` to imitate
+  /// WGSL syntax.
+  using f32 = float;
+
   /// Constructor
   /// @param ctx the context to use in the builder
   explicit Builder(tint::Context* ctx);
   virtual ~Builder();
 
-  /// Sets the given context into the builder
-  /// @param ctx the context to set
-  void set_context(tint::Context* ctx) { ctx_ = ctx; }
-
-  /// Creates a new type
-  /// @param args the arguments to pass to the type constructor
-  /// @returns a registered pointer to the requested type
-  template <typename T, typename... ARGS>
-  ast::type::Type* type(ARGS&&... args) {
-    assert(ctx_);
-    return ctx_->type_mgr().Get(
-        std::make_unique<T>(std::forward<ARGS>(args)...));
-  }
-
-  /// @returns a pointer to the bool type
-  ast::type::BoolType* bool_type() {
-    return type<ast::type::BoolType>()->AsBool();
-  }
-
-  /// @returns a pointer to the f32 type
-  ast::type::F32Type* f32() { return type<ast::type::F32Type>()->AsF32(); }
-
-  /// @returns a pointer to the i32 type
-  ast::type::I32Type* i32() { return type<ast::type::I32Type>()->AsI32(); }
-
-  /// @param ty the type of the matrix components
-  /// @param rows the number of rows
-  /// @param cols the number of columns
-  /// @returns a pointer to the u32 type
-  ast::type::MatrixType* mat(ast::type::Type* ty,
-                             uint32_t rows,
-                             uint32_t cols) {
-    return type<ast::type::MatrixType>(ty, rows, cols)->AsMatrix();
-  }
-
-  /// @returns a pointer to the u32 type
-  ast::type::U32Type* u32() { return type<ast::type::U32Type>()->AsU32(); }
-
-  /// @param ty the type of the vector components
-  /// @param size the size of the vector
-  /// @returns a pointer to the vector type
-  ast::type::VectorType* vec(ast::type::Type* ty, uint32_t size) {
-    return type<ast::type::VectorType>(ty, size)->AsVector();
-  }
-
-  /// @returns a pointer to the void type
-  ast::type::VoidType* void_type() {
-    return type<ast::type::VoidType>()->AsVoid();
-  }
-
   /// @param expr the expression
   /// @return expr
-  ast::Expression* make_expr(ast::Expression* expr) { return expr; }
+  ast::Expression* Expr(ast::Expression* expr) { return expr; }
 
   /// @param name the identifier name
   /// @return an IdentifierExpression with the given name
-  ast::IdentifierExpression* make_expr(const std::string& name) {
+  ast::IdentifierExpression* Expr(const std::string& name) {
     return create<ast::IdentifierExpression>(name);
   }
 
   /// @param name the identifier name
   /// @return an IdentifierExpression with the given name
-  ast::IdentifierExpression* make_expr(const char* name) {
+  ast::IdentifierExpression* Expr(const char* name) {
     return create<ast::IdentifierExpression>(name);
   }
 
   /// @param value the float value
   /// @return a Scalar constructor for the given value
-  ast::ScalarConstructorExpression* make_expr(float value) {
-    return create<ast::ScalarConstructorExpression>(make_literal(value));
+  ast::ScalarConstructorExpression* Expr(f32 value) {
+    return create<ast::ScalarConstructorExpression>(Literal(value));
   }
 
-  /// @param value the int value
+  /// @param value the integer value
   /// @return a Scalar constructor for the given value
-  ast::ScalarConstructorExpression* make_expr(int32_t value) {
-    return create<ast::ScalarConstructorExpression>(make_literal(value));
+  ast::ScalarConstructorExpression* Expr(i32 value) {
+    return create<ast::ScalarConstructorExpression>(Literal(value));
   }
 
   /// @param value the unsigned int value
   /// @return a Scalar constructor for the given value
-  ast::ScalarConstructorExpression* make_expr(uint32_t value) {
-    return create<ast::ScalarConstructorExpression>(make_literal(value));
+  ast::ScalarConstructorExpression* Expr(u32 value) {
+    return create<ast::ScalarConstructorExpression>(Literal(value));
   }
 
-  /// @param val the boolan value
-  /// @return a boolean literal with the given value
-  ast::BoolLiteral* make_literal(bool val) {
-    return create<ast::BoolLiteral>(bool_type(), val);
-  }
-
-  /// @param val the float value
-  /// @return a float literal with the given value
-  ast::FloatLiteral* make_literal(float val) {
-    return create<ast::FloatLiteral>(f32(), val);
-  }
-
-  /// @param val the unsigned int value
-  /// @return a UintLiteral with the given value
-  ast::UintLiteral* make_literal(uint32_t val) {
-    return create<ast::UintLiteral>(u32(), val);
-  }
-
-  /// @param val the integer value
-  /// @return the SintLiteral with the given value
-  ast::SintLiteral* make_literal(int32_t val) {
-    return create<ast::SintLiteral>(i32(), val);
-  }
-
-  /// Converts `arg` to an `ast::Expression` using `make_expr()`, then appends
-  /// it to `list`.
+  /// Converts `arg` to an `ast::Expression` using `Expr()`, then appends it to
+  /// `list`.
   /// @param list the list to append too
   /// @param arg the arg to create
   template <typename ARG>
-  void append_expr(ast::ExpressionList& list, ARG&& arg) {
-    list.emplace_back(make_expr(std::forward<ARG>(arg)));
+  void Append(ast::ExpressionList& list, ARG&& arg) {
+    list.emplace_back(Expr(std::forward<ARG>(arg)));
   }
 
-  /// Converts `arg0` and `args` to `ast::Expression`s using `make_expr()`,
+  /// Converts `arg0` and `args` to `ast::Expression`s using `Expr()`,
   /// then appends them to `list`.
   /// @param list the list to append too
   /// @param arg0 the first argument
   /// @param args the rest of the arguments
   template <typename ARG0, typename... ARGS>
-  void append_expr(ast::ExpressionList& list, ARG0&& arg0, ARGS&&... args) {
-    append_expr(list, std::forward<ARG0>(arg0));
-    append_expr(list, std::forward<ARGS>(args)...);
+  void Append(ast::ExpressionList& list, ARG0&& arg0, ARGS&&... args) {
+    Append(list, std::forward<ARG0>(arg0));
+    Append(list, std::forward<ARGS>(args)...);
   }
 
-  /// @param ty the type
+  /// @param args the list of expressions
+  /// @return the list of expressions converted to `ast::Expression`s using
+  /// `Expr()`,
+  template <typename... ARGS>
+  ast::ExpressionList ExprList(ARGS&&... args) {
+    ast::ExpressionList list;
+    list.reserve(sizeof...(args));
+    Append(list, std::forward<ARGS>(args)...);
+    return list;
+  }
+
+  /// @param val the boolan value
+  /// @return a boolean literal with the given value
+  ast::BoolLiteral* Literal(bool val) {
+    return create<ast::BoolLiteral>(ty.bool_, val);
+  }
+
+  /// @param val the float value
+  /// @return a float literal with the given value
+  ast::FloatLiteral* Literal(f32 val) {
+    return create<ast::FloatLiteral>(ty.f32, val);
+  }
+
+  /// @param val the unsigned int value
+  /// @return a UintLiteral with the given value
+  ast::UintLiteral* Literal(u32 val) {
+    return create<ast::UintLiteral>(ty.u32, val);
+  }
+
+  /// @param val the integer value
+  /// @return the SintLiteral with the given value
+  ast::SintLiteral* Literal(i32 val) {
+    return create<ast::SintLiteral>(ty.i32, val);
+  }
+
   /// @param args the arguments for the type constructor
   /// @return an `ast::TypeConstructorExpression` of type `ty`, with the values
-  /// of `args` converted to `ast::Expression`s using `make_expr()`
-  template <typename... ARGS>
-  ast::TypeConstructorExpression* construct(ast::type::Type* ty,
-                                            ARGS&&... args) {
-    ast::ExpressionList vals;
-    append_expr(vals, std::forward<ARGS>(args)...);
-    return create<ast::TypeConstructorExpression>(ty, std::move(vals));
+  /// of `args` converted to `ast::Expression`s using `Expr()`
+  template <typename T, typename... ARGS>
+  ast::TypeConstructorExpression* Construct(ARGS&&... args) {
+    return create<ast::TypeConstructorExpression>(
+        ty.Of<T>(), ExprList(std::forward<ARGS>(args)...));
+  }
+
+  /// @param x the first component of the vector
+  /// @param y the second component of the vector
+  /// @return an `ast::TypeConstructorExpression` of a 2-element vector of type
+  /// `T`, constructed with the values `x` and `y`.
+  template <typename T>
+  ast::TypeConstructorExpression* vec2(T&& x, T&& y) {
+    return create<ast::TypeConstructorExpression>(
+        ty.vec2<T>(), ExprList(std::move(x), std::move(y)));
+  }
+
+  /// @param x the first component of the vector
+  /// @param y the second component of the vector
+  /// @param z the third component of the vector
+  /// @return an `ast::TypeConstructorExpression` of a 3-element vector of type
+  /// `T`, constructed with the values `x`, `y` and `z`.
+  template <typename T>
+  ast::TypeConstructorExpression* vec3(T&& x, T&& y, T&& z) {
+    return create<ast::TypeConstructorExpression>(
+        ty.vec3<T>(), ExprList(std::move(x), std::move(y), std::move(z)));
+  }
+
+  /// @param x the first component of the vector
+  /// @param y the second component of the vector
+  /// @param z the third component of the vector
+  /// @param w the fourth component of the vector
+  /// @return an `ast::TypeConstructorExpression` of a 4-element vector of type
+  /// `T`, constructed with the values `x`, `y`, `z` and `w`.
+  template <typename T>
+  ast::TypeConstructorExpression* vec4(T&& x, T&& y, T&& z, T&& w) {
+    return create<ast::TypeConstructorExpression>(
+        ty.vec4<T>(),
+        ExprList(std::move(x), std::move(y), std::move(z), std::move(w)));
   }
 
   /// @param name the variable name
   /// @param storage the variable storage class
   /// @param type the variable type
   /// @returns a `ast::Variable` with the given name, storage and type
-  virtual ast::Variable* make_var(const std::string& name,
-                                  ast::StorageClass storage,
-                                  ast::type::Type* type);
+  ast::Variable* Var(const std::string& name,
+                     ast::StorageClass storage,
+                     ast::type::Type* type);
 
   /// @param func the function name
   /// @param args the function call arguments
   /// @returns a `ast::CallExpression` to the function `func`, with the
-  /// arguments of `args` converted to `ast::Expression`s using `make_expr()`.
+  /// arguments of `args` converted to `ast::Expression`s using `Expr()`.
   template <typename... ARGS>
-  ast::CallExpression call_expr(const std::string& func, ARGS&&... args) {
-    ast::ExpressionList params;
-    append_expr(params, std::forward<ARGS>(args)...);
-    return ast::CallExpression{make_expr(func), std::move(params)};
+  ast::CallExpression Call(const std::string& func, ARGS&&... args) {
+    return ast::CallExpression{Expr(func),
+                               ExprList(std::forward<ARGS>(args)...)};
   }
 
   /// Creates a new `ast::Node` owned by the Context. When the Context is
@@ -219,13 +276,76 @@
   /// @returns the node pointer
   template <typename T, typename... ARGS>
   T* create(ARGS&&... args) {
-    return ctx_->create<T>(std::forward<ARGS>(args)...);
+    return ctx->create<T>(std::forward<ARGS>(args)...);
   }
 
- private:
-  tint::Context* ctx_ = nullptr;
+  /// The builder context
+  tint::Context* const ctx;
+  /// The builder types
+  const TypesBuilder ty;
+
+ protected:
+  /// Called whenever a new variable is built with `Var()`.
+  virtual void OnVariableBuilt(ast::Variable*) {}
 };
 
+template <typename T>
+ast::type::Type* TypesBuilder::Of() const {
+  return CToAST<T>::get(this);
+}
+
+template <typename T>
+ast::type::Type* TypesBuilder::vec2() const {
+  return tm_->Get<ast::type::VectorType>(Of<T>(), 2);
+}
+template <typename T>
+ast::type::Type* TypesBuilder::vec3() const {
+  return tm_->Get<ast::type::VectorType>(Of<T>(), 3);
+}
+template <typename T>
+ast::type::Type* TypesBuilder::vec4() const {
+  return tm_->Get<ast::type::VectorType>(Of<T>(), 4);
+}
+template <typename T>
+ast::type::Type* TypesBuilder::mat3x3() const {
+  return tm_->Get<ast::type::MatrixType>(Of<T>(), 3, 3);
+}
+template <typename T>
+ast::type::Type* TypesBuilder::arr() const {
+  return tm_->Get<ast::type::ArrayType>(Of<T>());
+}
+
+/// BuilderWithContext is a `Builder` that constructs and owns its `Context`.
+class BuilderWithContext : public Builder {
+ public:
+  BuilderWithContext();
+  ~BuilderWithContext() override;
+};
+
+//! @cond Doxygen_Suppress
+// Various template specializations for TypesBuilder::CToAST.
+template <>
+struct TypesBuilder::CToAST<Builder::i32> {
+  static ast::type::Type* get(const TypesBuilder* t) { return t->i32; }
+};
+template <>
+struct TypesBuilder::CToAST<Builder::u32> {
+  static ast::type::Type* get(const TypesBuilder* t) { return t->u32; }
+};
+template <>
+struct TypesBuilder::CToAST<Builder::f32> {
+  static ast::type::Type* get(const TypesBuilder* t) { return t->f32; }
+};
+template <>
+struct TypesBuilder::CToAST<bool> {
+  static ast::type::Type* get(const TypesBuilder* t) { return t->bool_; }
+};
+template <>
+struct TypesBuilder::CToAST<void> {
+  static ast::type::Type* get(const TypesBuilder* t) { return t->void_; }
+};
+//! @endcond
+
 }  // namespace ast
 }  // namespace tint
 
diff --git a/src/type_manager.h b/src/type_manager.h
index 2c82d76..f7f5601 100644
--- a/src/type_manager.h
+++ b/src/type_manager.h
@@ -18,6 +18,7 @@
 #include <memory>
 #include <string>
 #include <unordered_map>
+#include <utility>
 
 #include "src/ast/type/type.h"
 
@@ -37,6 +38,15 @@
   /// @return the pointer to the registered type
   ast::type::Type* Get(std::unique_ptr<ast::type::Type> type);
 
+  /// Get the given type `T` from the type manager
+  /// @param args the arguments to pass to the type constructor
+  /// @return the pointer to the registered type
+  template <typename T, typename... ARGS>
+  T* Get(ARGS&&... args) {
+    auto ty = Get(std::make_unique<T>(std::forward<ARGS>(args)...));
+    return static_cast<T*>(ty);
+  }
+
   /// Returns the type map
   /// @returns the mapping from name string to type.
   const std::unordered_map<std::string, std::unique_ptr<ast::type::Type>>&
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 695bd85..631e604 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -45,29 +45,22 @@
 #include "src/type_determiner.h"
 #include "src/writer/spirv/builder.h"
 #include "src/writer/spirv/spv_dump.h"
-#include "src/writer/spirv/test_helper.h"
 
 namespace tint {
 namespace writer {
 namespace spirv {
 namespace {
 
-class IntrinsicBuilderTest : public ast::Builder, public testing::Test {
- public:
-  IntrinsicBuilderTest() { set_context(&ctx); }
-
-  ast::Variable* make_var(const std::string& name,
-                          ast::StorageClass storage,
-                          ast::type::Type* type) override {
-    auto* var = ast::Builder::make_var(name, storage, type);
+class IntrinsicBuilderTest : public ast::BuilderWithContext,
+                             public testing::Test {
+ protected:
+  void OnVariableBuilt(ast::Variable* var) override {
     td.RegisterVariableForTesting(var);
-    return var;
   }
 
-  Context ctx;
   ast::Module mod;
-  TypeDeterminer td{&ctx, &mod};
-  spirv::Builder b{&ctx, &mod};
+  TypeDeterminer td{ctx, &mod};
+  spirv::Builder b{ctx, &mod};
 };
 
 template <typename T>
@@ -87,8 +80,8 @@
 TEST_P(IntrinsicBoolTest, Call_Bool) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, vec(bool_type(), 3));
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.vec3<bool>());
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -116,8 +109,8 @@
 TEST_P(IntrinsicFloatTest, Call_Float_Scalar) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, f32());
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.f32);
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -140,8 +133,8 @@
 TEST_P(IntrinsicFloatTest, Call_Float_Vector) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, vec(f32(), 3));
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.vec3<f32>());
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -171,8 +164,8 @@
 TEST_P(IntrinsicIntTest, Call_SInt_Scalar) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, i32());
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.i32);
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -194,8 +187,8 @@
 TEST_P(IntrinsicIntTest, Call_SInt_Vector) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, vec(i32(), 3));
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.vec3<i32>());
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -218,8 +211,8 @@
 TEST_P(IntrinsicIntTest, Call_UInt_Scalar) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, u32());
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.u32);
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -241,8 +234,8 @@
 TEST_P(IntrinsicIntTest, Call_UInt_Vector) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, vec(u32(), 3));
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.vec3<u32>());
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -268,8 +261,8 @@
                     IntrinsicData{"reverseBits", "OpBitReverse"}));
 
 TEST_F(IntrinsicBuilderTest, Call_Dot) {
-  auto* var = make_var("v", ast::StorageClass::kPrivate, vec(f32(), 3));
-  auto expr = call_expr("dot", "v", "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.vec3<f32>());
+  auto expr = Call("dot", "v", "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -294,8 +287,8 @@
 TEST_P(IntrinsicDeriveTest, Call_Derivative_Scalar) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, f32());
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.f32);
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -317,8 +310,8 @@
 TEST_P(IntrinsicDeriveTest, Call_Derivative_Vector) {
   auto param = GetParam();
 
-  auto* var = make_var("v", ast::StorageClass::kPrivate, vec(f32(), 3));
-  auto expr = call_expr(param.name, "v");
+  auto* var = Var("v", ast::StorageClass::kPrivate, ty.vec3<f32>());
+  auto expr = Call(param.name, "v");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -358,10 +351,10 @@
                     IntrinsicData{"fwidthCoarse", "OpFwidthCoarse"}));
 
 TEST_F(IntrinsicBuilderTest, Call_OuterProduct) {
-  auto* v2 = make_var("v2", ast::StorageClass::kPrivate, vec(f32(), 2));
-  auto* v3 = make_var("v3", ast::StorageClass::kPrivate, vec(f32(), 3));
+  auto* v2 = Var("v2", ast::StorageClass::kPrivate, ty.vec2<f32>());
+  auto* v3 = Var("v3", ast::StorageClass::kPrivate, ty.vec3<f32>());
 
-  auto expr = call_expr("outerProduct", "v2", "v3");
+  auto expr = Call("outerProduct", "v2", "v3");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -390,10 +383,9 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Select) {
-  auto* v3 = make_var("v3", ast::StorageClass::kPrivate, vec(f32(), 3));
-  auto* bool_v3 =
-      make_var("bool_v3", ast::StorageClass::kPrivate, vec(bool_type(), 3));
-  auto expr = call_expr("select", "v3", "v3", "bool_v3");
+  auto* v3 = Var("v3", ast::StorageClass::kPrivate, ty.vec3<f32>());
+  auto* bool_v3 = Var("bool_v3", ast::StorageClass::kPrivate, ty.vec3<bool>());
+  auto expr = Call("select", "v3", "v3", "bool_v3");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
@@ -431,10 +423,10 @@
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &s);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto expr = call_expr("textureLoad", "texture", 1.0f, 2);
+  auto expr = Call("textureLoad", "texture", 1.0f, 2);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
@@ -465,11 +457,10 @@
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &s);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto expr = call_expr("textureLoad", "texture",
-                        construct(vec(f32(), 2), 1.0f, 2.0f), 2);
+  auto expr = Call("textureLoad", "texture", vec2<f32>(1.0f, 2.0f), 2);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
@@ -495,14 +486,14 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_TextureLoad_Sampled_1d) {
-  ast::type::SampledTextureType s(ast::type::TextureDimension::k1d, f32());
+  ast::type::SampledTextureType s(ast::type::TextureDimension::k1d, ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &s);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto expr = call_expr("textureLoad", "texture", 1.0f, 2);
+  auto expr = Call("textureLoad", "texture", 1.0f, 2);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
@@ -525,15 +516,14 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_TextureLoad_Sampled_2d) {
-  ast::type::SampledTextureType s(ast::type::TextureDimension::k2d, f32());
+  ast::type::SampledTextureType s(ast::type::TextureDimension::k2d, ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &s);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto expr = call_expr("textureLoad", "texture",
-                        construct(vec(f32(), 2), 1.0f, 2.0f), 2);
+  auto expr = Call("textureLoad", "texture", vec2<f32>(1.0f, 2.0f), 2);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
@@ -559,15 +549,15 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_TextureLoad_Multisampled_2d) {
-  ast::type::MultisampledTextureType s(ast::type::TextureDimension::k2d, f32());
+  ast::type::MultisampledTextureType s(ast::type::TextureDimension::k2d,
+                                       ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &s);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto expr = call_expr("textureLoad", "texture",
-                        construct(vec(f32(), 2), 1.0f, 2.0f), 2);
+  auto expr = Call("textureLoad", "texture", vec2<f32>(1.0f, 2.0f), 2);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
@@ -598,13 +588,13 @@
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSample", "texture", "sampler", 1.0f);
+  auto expr = Call("textureSample", "texture", "sampler", 1.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 7u) << b.error();
@@ -635,14 +625,14 @@
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSample", "texture", "sampler",
-                        construct(vec(f32(), 2), 1.0f, 2.0f));
+  auto expr =
+      Call("textureSample", "texture", "sampler", vec2<f32>(1.0f, 2.0f));
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 7u) << b.error();
@@ -672,17 +662,17 @@
 
 TEST_F(IntrinsicBuilderTest, Call_TextureSampleLevel_1d) {
   ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, f32());
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSampleLevel", "texture", "sampler", 1.0f, 2.0f);
+  auto expr = Call("textureSampleLevel", "texture", "sampler", 1.0f, 2.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
@@ -711,18 +701,18 @@
 
 TEST_F(IntrinsicBuilderTest, Call_TextureSampleLevel_2d) {
   ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k2d, f32());
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k2d, ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSampleLevel", "texture", "sampler",
-                        construct(vec(f32(), 2), 1.0f, 2.0f), 2.0f);
+  auto expr = Call("textureSampleLevel", "texture", "sampler",
+                   vec2<f32>(1.0f, 2.0f), 2.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
@@ -753,17 +743,17 @@
 
 TEST_F(IntrinsicBuilderTest, Call_TextureSampleBias_1d) {
   ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, f32());
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSampleBias", "texture", "sampler", 1.0f, 2.0f);
+  auto expr = Call("textureSampleBias", "texture", "sampler", 1.0f, 2.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
@@ -792,18 +782,18 @@
 
 TEST_F(IntrinsicBuilderTest, Call_TextureSampleBias_2d) {
   ast::type::SamplerType s(ast::type::SamplerKind::kSampler);
-  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, f32());
+  ast::type::SampledTextureType t(ast::type::TextureDimension::k1d, ty.f32);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSampleBias", "texture", "sampler",
-                        construct(vec(f32(), 2), 1.0f, 2.0f), 2.0f);
+  auto expr = Call("textureSampleBias", "texture", "sampler",
+                   vec2<f32>(1.0f, 2.0f), 2.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
@@ -838,14 +828,14 @@
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr = call_expr("textureSampleCompare", "texture", "sampler",
-                        construct(vec(f32(), 2), 1.0f, 2.0f), 2.0f);
+  auto expr = Call("textureSampleCompare", "texture", "sampler",
+                   vec2<f32>(1.0f, 2.0f), 2.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_EQ(b.GenerateExpression(&expr), 8u) << b.error();
@@ -875,24 +865,22 @@
 
 // This tests that we do not push OpTypeSampledImage and float_0 type twice.
 TEST_F(IntrinsicBuilderTest, Call_TextureSampleCompare_Twice) {
-  auto* vec2 = vec(f32(), 2);
-
   ast::type::SamplerType s(ast::type::SamplerKind::kComparisonSampler);
   ast::type::DepthTextureType t(ast::type::TextureDimension::k2d);
 
   b.push_function(Function{});
 
-  auto* tex = make_var("texture", ast::StorageClass::kNone, &t);
+  auto* tex = Var("texture", ast::StorageClass::kNone, &t);
   ASSERT_TRUE(b.GenerateGlobalVariable(tex)) << b.error();
 
-  auto* sampler = make_var("sampler", ast::StorageClass::kNone, &s);
+  auto* sampler = Var("sampler", ast::StorageClass::kNone, &s);
   ASSERT_TRUE(b.GenerateGlobalVariable(sampler)) << b.error();
 
-  auto expr1 = call_expr("textureSampleCompare", "texture", "sampler",
-                         construct(vec2, 1.0f, 2.0f), 2.0f);
+  auto expr1 = Call("textureSampleCompare", "texture", "sampler",
+                    vec2<f32>(1.0f, 2.0f), 2.0f);
 
-  auto expr2 = call_expr("textureSampleCompare", "texture", "sampler",
-                         construct(vec2, 1.0f, 2.0f), 2.0f);
+  auto expr2 = Call("textureSampleCompare", "texture", "sampler",
+                    vec2<f32>(1.0f, 2.0f), 2.0f);
 
   EXPECT_TRUE(td.DetermineResultType(&expr1)) << td.error();
   EXPECT_TRUE(td.DetermineResultType(&expr2)) << td.error();
@@ -928,12 +916,12 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_GLSLMethod_WithLoad) {
-  auto* var = make_var("ident", ast::StorageClass::kPrivate, f32());
-  auto expr = call_expr("round", "ident");
+  auto* var = Var("ident", ast::StorageClass::kPrivate, ty.f32);
+  auto expr = Call("round", "ident");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
@@ -961,10 +949,10 @@
 TEST_P(Intrinsic_Builtin_SingleParam_Float_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1.0f);
+  auto expr = Call(param.name, 1.0f);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -986,10 +974,10 @@
 TEST_P(Intrinsic_Builtin_SingleParam_Float_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, construct(vec(f32(), 2), 1.0f, 1.0f));
+  auto expr = Call(param.name, vec2<f32>(1.0f, 1.0f));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1036,11 +1024,11 @@
                                          IntrinsicData{"trunc", "Trunc"}));
 
 TEST_F(IntrinsicBuilderTest, Call_Length_Scalar) {
-  auto expr = call_expr("length", 1.0f);
+  auto expr = Call("length", 1.0f);
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1059,10 +1047,10 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Length_Vector) {
-  auto expr = call_expr("length", construct(vec(f32(), 2), 1.0f, 1.0f));
+  auto expr = Call("length", vec2<f32>(1.0f, 1.0f));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1083,10 +1071,10 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Normalize) {
-  auto expr = call_expr("normalize", construct(vec(f32(), 2), 1.0f, 1.0f));
+  auto expr = Call("normalize", vec2<f32>(1.0f, 1.0f));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1111,10 +1099,11 @@
 TEST_P(Intrinsic_Builtin_DualParam_Float_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1.0f, 1.0f);
+  auto expr = Call(param.name, 1.0f, 1.0f);
+
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1136,13 +1125,11 @@
 TEST_P(Intrinsic_Builtin_DualParam_Float_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto* vec2 = vec(f32(), 2);
-  auto expr = call_expr(param.name, construct(vec2, 1.0f, 1.0f),
-                        construct(vec2, 1.0f, 1.0f));
+  auto expr = Call(param.name, vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1172,10 +1159,11 @@
                                          IntrinsicData{"step", "Step"}));
 
 TEST_F(IntrinsicBuilderTest, Call_Distance_Scalar) {
-  auto expr = call_expr("distance", 1.0f, 1.0f);
+  auto expr = Call("distance", 1.0f, 1.0f);
+
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1194,13 +1182,11 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Distance_Vector) {
-  auto* vec3 = vec(f32(), 2);
-  auto expr = call_expr("distance", construct(vec3, 1.0f, 1.0f),
-                        construct(vec3, 1.0f, 1.0f));
+  auto expr = Call("distance", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1221,13 +1207,12 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Cross) {
-  auto* vec3 = vec(f32(), 3);
-  auto expr = call_expr("cross", construct(vec3, 1.0f, 1.0f, 1.0f),
-                        construct(vec3, 1.0f, 1.0f, 1.0f));
+  auto expr =
+      Call("cross", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1252,10 +1237,10 @@
 TEST_P(Intrinsic_Builtin_ThreeParam_Float_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1.0f, 1.0f, 1.0f);
+  auto expr = Call(param.name, 1.0f, 1.0f, 1.0f);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1277,14 +1262,12 @@
 TEST_P(Intrinsic_Builtin_ThreeParam_Float_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto* vec3 = vec(f32(), 2);
-  auto expr =
-      call_expr(param.name, construct(vec3, 1.0f, 1.0f),
-                construct(vec3, 1.0f, 1.0f), construct(vec3, 1.0f, 1.0f));
+  auto expr = Call(param.name, vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f),
+                   vec2<f32>(1.0f, 1.0f));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1319,10 +1302,10 @@
 TEST_P(Intrinsic_Builtin_SingleParam_Sint_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1);
+  auto expr = Call(param.name, 1);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1344,10 +1327,10 @@
 TEST_P(Intrinsic_Builtin_SingleParam_Sint_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, construct(vec(i32(), 2), 1, 1));
+  auto expr = Call(param.name, vec2<i32>(1, 1));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1376,10 +1359,10 @@
 TEST_P(Intrinsic_Builtin_SingleParam_Uint_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1u);
+  auto expr = Call(param.name, 1u);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1401,10 +1384,10 @@
 TEST_P(Intrinsic_Builtin_SingleParam_Uint_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, construct(vec(u32(), 2), 1u, 1u));
+  auto expr = Call(param.name, vec2<u32>(1u, 1u));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1433,10 +1416,10 @@
 TEST_P(Intrinsic_Builtin_DualParam_SInt_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1, 1);
+  auto expr = Call(param.name, 1, 1);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1458,12 +1441,10 @@
 TEST_P(Intrinsic_Builtin_DualParam_SInt_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto* vec2 = vec(i32(), 2);
-  auto expr =
-      call_expr(param.name, construct(vec2, 1, 1), construct(vec2, 1, 1));
+  auto expr = Call(param.name, vec2<i32>(1, 1), vec2<i32>(1, 1));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1493,10 +1474,10 @@
 TEST_P(Intrinsic_Builtin_DualParam_UInt_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1u, 1u);
+  auto expr = Call(param.name, 1u, 1u);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1518,13 +1499,10 @@
 TEST_P(Intrinsic_Builtin_DualParam_UInt_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto* vec2 = vec(u32(), 2);
-  auto expr =
-      call_expr(param.name, construct(vec2, 1u, 1u), construct(vec2, 1u, 1u));
-
+  auto expr = Call(param.name, vec2<u32>(1u, 1u), vec2<u32>(1u, 1u));
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1554,10 +1532,10 @@
 TEST_P(Intrinsic_Builtin_ThreeParam_Sint_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1, 1, 1);
+  auto expr = Call(param.name, 1, 1, 1);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1579,13 +1557,12 @@
 TEST_P(Intrinsic_Builtin_ThreeParam_Sint_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto* vec2 = vec(i32(), 2);
-  auto expr = call_expr(param.name, construct(vec2, 1, 1),
-                        construct(vec2, 1, 1), construct(vec2, 1, 1));
+  auto expr =
+      Call(param.name, vec2<i32>(1, 1), vec2<i32>(1, 1), vec2<i32>(1, 1));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1614,10 +1591,10 @@
 TEST_P(Intrinsic_Builtin_ThreeParam_Uint_Test, Call_Scalar) {
   auto param = GetParam();
 
-  auto expr = call_expr(param.name, 1u, 1u, 1u);
+  auto expr = Call(param.name, 1u, 1u, 1u);
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1639,13 +1616,12 @@
 TEST_P(Intrinsic_Builtin_ThreeParam_Uint_Test, Call_Vector) {
   auto param = GetParam();
 
-  auto* vec2 = vec(u32(), 2);
-  auto expr = call_expr(param.name, construct(vec2, 1u, 1u),
-                        construct(vec2, 1u, 1u), construct(vec2, 1u, 1u));
+  auto expr =
+      Call(param.name, vec2<u32>(1u, 1u), vec2<u32>(1u, 1u), vec2<u32>(1u, 1u));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1670,12 +1646,12 @@
                          testing::Values(IntrinsicData{"clamp", "UClamp"}));
 
 TEST_F(IntrinsicBuilderTest, Call_Determinant) {
-  auto* var = make_var("var", ast::StorageClass::kPrivate, mat(f32(), 3, 3));
-  auto expr = call_expr("determinant", "var");
+  auto* var = Var("var", ast::StorageClass::kPrivate, ty.mat3x3<f32>());
+  auto expr = Call("determinant", "var");
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
 
@@ -1702,23 +1678,21 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_ArrayLength) {
-  ast::type::ArrayType ary(f32());
-
   ast::StructMemberDecorationList decos;
   ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>("a", &ary, decos));
+  members.push_back(create<ast::StructMember>("a", ty.arr<f32>(), decos));
 
   auto* s = create<ast::Struct>(members);
   ast::type::StructType s_type("my_struct", s);
 
-  auto* var = make_var("b", ast::StorageClass::kPrivate, &s_type);
+  auto* var = Var("b", ast::StorageClass::kPrivate, &s_type);
 
-  auto expr = call_expr("arrayLength", create<ast::MemberAccessorExpression>(
-                                           make_expr("b"), make_expr("a")));
+  auto expr = Call("arrayLength",
+                   create<ast::MemberAccessorExpression>(Expr("b"), Expr("a")));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
@@ -1742,23 +1716,21 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_ArrayLength_OtherMembersInStruct) {
-  ast::type::ArrayType ary(f32());
-
   ast::StructMemberDecorationList decos;
   ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>("z", f32(), decos));
-  members.push_back(create<ast::StructMember>("a", &ary, decos));
+  members.push_back(create<ast::StructMember>("z", ty.f32, decos));
+  members.push_back(create<ast::StructMember>("a", ty.arr<f32>(), decos));
 
   auto* s = create<ast::Struct>(members);
   ast::type::StructType s_type("my_struct", s);
 
-  auto* var = make_var("b", ast::StorageClass::kPrivate, &s_type);
-  auto expr = call_expr("arrayLength", create<ast::MemberAccessorExpression>(
-                                           make_expr("b"), make_expr("a")));
+  auto* var = Var("b", ast::StorageClass::kPrivate, &s_type);
+  auto expr = Call("arrayLength",
+                   create<ast::MemberAccessorExpression>(Expr("b"), Expr("a")));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
@@ -1783,27 +1755,26 @@
 
 // TODO(dsinclair): https://bugs.chromium.org/p/tint/issues/detail?id=266
 TEST_F(IntrinsicBuilderTest, DISABLED_Call_ArrayLength_Ptr) {
-  ast::type::ArrayType ary(f32());
-  ast::type::PointerType ptr(&ary, ast::StorageClass::kStorageBuffer);
+  ast::type::PointerType ptr(ty.arr<f32>(), ast::StorageClass::kStorageBuffer);
 
   ast::StructMemberDecorationList decos;
   ast::StructMemberList members;
-  members.push_back(create<ast::StructMember>("z", f32(), decos));
-  members.push_back(create<ast::StructMember>("a", &ary, decos));
+  members.push_back(create<ast::StructMember>("z", ty.f32, decos));
+  members.push_back(create<ast::StructMember>("a", ty.arr<f32>(), decos));
 
   auto* s = create<ast::Struct>(members);
   ast::type::StructType s_type("my_struct", s);
 
-  auto* var = make_var("b", ast::StorageClass::kPrivate, &s_type);
+  auto* var = Var("b", ast::StorageClass::kPrivate, &s_type);
 
-  auto* ptr_var = make_var("ptr_var", ast::StorageClass::kPrivate, &ptr);
+  auto* ptr_var = Var("ptr_var", ast::StorageClass::kPrivate, &ptr);
   ptr_var->set_constructor(
-      create<ast::MemberAccessorExpression>(make_expr("b"), make_expr("a")));
+      create<ast::MemberAccessorExpression>(Expr("b"), Expr("a")));
 
-  auto expr = call_expr("arrayLength", "ptr_var");
+  auto expr = Call("arrayLength", "ptr_var");
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
-  ast::Function func("a_func", {}, void_type(), create<ast::BlockStatement>());
+  ast::Function func("a_func", {}, ty.void_, create<ast::BlockStatement>());
 
   ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();