Rename TypeDeterminer to Resolver

Move out of the src root and into its own subdirectory
Rename methods to remove the 'Determine' prefix.

Fixed: tint:529
Change-Id: Idf89d647780f8a2e7495c1c9e6c402e00ad45b7c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44041
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index e2c107e..d96332e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -377,6 +377,8 @@
     "src/program_builder.h",
     "src/reader/reader.cc",
     "src/reader/reader.h",
+    "src/resolver/resolver.cc",
+    "src/resolver/resolver.h",
     "src/scope_stack.h",
     "src/semantic/call.h",
     "src/semantic/expression.h",
@@ -455,8 +457,6 @@
     "src/type/vector_type.h",
     "src/type/void_type.cc",
     "src/type/void_type.h",
-    "src/type_determiner.cc",
-    "src/type_determiner.h",
     "src/utils/unique_vector.h",
     "src/validator/validator.cc",
     "src/validator/validator.h",
@@ -842,6 +842,7 @@
     "src/intrinsic_table_test.cc",
     "src/program_builder_test.cc",
     "src/program_test.cc",
+    "src/resolver/resolver_test.cc",
     "src/semantic/sem_intrinsic_test.cc",
     "src/scope_stack_test.cc",
     "src/symbol_table_test.cc",
@@ -871,7 +872,6 @@
     "src/type/type_manager_test.cc",
     "src/type/u32_type_test.cc",
     "src/type/vector_type_test.cc",
-    "src/type_determiner_test.cc",
     "src/utils/command_test.cc",
     "src/utils/command.h",
     "src/utils/tmpfile_test.cc",
diff --git a/docs/arch.md b/docs/arch.md
index 81385fa..39d14cb 100644
--- a/docs/arch.md
+++ b/docs/arch.md
@@ -24,7 +24,7 @@
       ┃             ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
       ▲             ┆ Build           ▼                ┆
   ┏━━━┻━━━┓         ┆        ┏━━━━━━━━┻━━━━━━━━┓       ┆
-  ┃ Clone ┃         ┆        ┃ Type Determiner ┃       ┆
+  ┃ Clone ┃         ┆        ┃    Resolver     ┃       ┆
   ┗━━━┳━━━┛         ┆        ┗━━━━━━━━━━━━━━━━━┛       ┆
       ▲             └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
       ┃                               ▼
@@ -69,8 +69,8 @@
 the `Program` is constructed.
 
 A `Program` is built from the `ProgramBuilder` by `std::move()`ing the
-`ProgramBuilder` to a new `Program` object. When built, type determination is
-run so the produced `Program` will contain all the needed semantic information.
+`ProgramBuilder` to a new `Program` object. When built, resolution is performed
+so the produced `Program` will contain all the needed semantic information.
 
 At any time before building the `Program`, `ProgramBuilder::IsValid()` may be
 called to ensure the AST is **structurally** correct. This checks that things
@@ -101,7 +101,7 @@
 
 ## Types
 
-Types are constructed during the Reader and Type Determination phases, and are
+Types are constructed during the Reader and resolution phases, and are
 held by the `Program` or `ProgramBuilder`. AST and semantic nodes can both
 reference types.
 
@@ -120,7 +120,7 @@
 the resolved type of each expression, the resolved overload of an intrinsic
 function call, and the module scoped variables used by each function.
 
-Semantic information is generated by the `TypeDeterminer` when the `Program`
+Semantic information is generated by the `Resolver` when the `Program`
 is built from a `ProgramBuilder`.
 
 The `semantic::Info` class holds a map of `ast::Node`s to `semantic::Node`s.
@@ -142,15 +142,15 @@
 A `Namer` may output the symbol in any form that preserves the uniqueness of
 that symbol.
 
-## Type Determiner
+## Resolver
 
-The `TypeDeterminer` will automatically run when a `Program` is built.
-A `TypeDeterminer` creates the `Program`s semantic information by analyzing the
+The `Resolver` will automatically run when a `Program` is built.
+A `Resolver` creates the `Program`s semantic information by analyzing the
 `Program`s AST and type information.
 
-The `TypeDeterminer` will do the minimal amount of validation required in order
+The `Resolver` will do the minimal amount of validation required in order
 to always be accessing valid nodes, reporting any errors found in the
-`Program`'s diagnostics. Even if the `TypeDeterminer` doesn't generate any
+`Program`'s diagnostics. Even if the `Resolver` doesn't generate any
 errors doesn't mean the generated `Program` is semantically valid. Use the
 `Validator` to check for a `Program`'s final validity.
 
@@ -158,10 +158,10 @@
 
 A `Program` holds an immutable version of the information from the
 `ProgramBuilder` along with semantic information generated by the
-`TypeDeterminer`.
+`Resolver`.
 
 Like `ProgramBuilder`, `Program::IsValid()` may be called to ensure the AST is
-structurally correct and that the `TypeDeterminer` did not report any errors.
+structurally correct and that the `Resolver` did not report any errors.
 `Program::IsValid()` does not perform semantic validation, use the `Validator`
 to check for a `Program`'s final validity.
 
@@ -192,7 +192,7 @@
 
 A transform operates by cloning the input `Program` into a new `ProgramBuilder`,
 applying the required changes, and then finally building and returning a new
-output `Program`. As type determination is always run when a `Program` is built,
+output `Program`. As the resolver is always run when a `Program` is built,
 Transforms will always emit a `Program` with semantic information.
 
 The input `Program` to a transform must be valid (pass validation).
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 11280af..ba20f11 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -191,6 +191,8 @@
   program.h
   reader/reader.cc
   reader/reader.h
+  resolver/resolver.cc
+  resolver/resolver.h
   scope_stack.h
   semantic/call.h
   semantic/expression.h
@@ -229,8 +231,6 @@
   transform/transform.h
   transform/vertex_pulling.cc
   transform/vertex_pulling.h
-  type_determiner.cc
-  type_determiner.h
   type/access_control_type.cc
   type/access_control_type.h
   type/alias_type.cc
@@ -470,13 +470,13 @@
     inspector/inspector_test.cc
     intrinsic_table_test.cc
     program_test.cc
+    resolver/resolver_test.cc
     semantic/sem_intrinsic_test.cc
     scope_stack_test.cc
     symbol_table_test.cc
     symbol_test.cc
     traits_test.cc
     test_main.cc
-    type_determiner_test.cc
     type/access_control_type_test.cc
     type/alias_type_test.cc
     type/array_type_test.cc
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index 9557bbc..1b8613b 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -60,7 +60,6 @@
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "tint/tint.h"
 
 namespace tint {
@@ -704,7 +703,6 @@
   type::Sampler* comparison_sampler_type() { return &comparison_sampler_type_; }
 
  private:
-  std::unique_ptr<TypeDeterminer> td_;
   std::unique_ptr<Program> program_;
   std::unique_ptr<Inspector> inspector_;
   type::Sampler sampler_type_;
diff --git a/src/program.cc b/src/program.cc
index cbf4b91..71fc7fe 100644
--- a/src/program.cc
+++ b/src/program.cc
@@ -21,8 +21,8 @@
 #include "src/clone_context.h"
 #include "src/demangler.h"
 #include "src/program_builder.h"
+#include "src/resolver/resolver.h"
 #include "src/semantic/expression.h"
-#include "src/type_determiner.h"
 
 namespace tint {
 
@@ -44,9 +44,9 @@
 Program::Program(ProgramBuilder&& builder) {
   is_valid_ = builder.IsValid();
   if (builder.ResolveOnBuild() && builder.IsValid()) {
-    TypeDeterminer td(&builder);
-    if (!td.Determine()) {
-      diagnostics_.add_error(td.error());
+    Resolver resolver(&builder);
+    if (!resolver.Resolve()) {
+      diagnostics_.add_error(resolver.error());
       is_valid_ = false;
     }
   }
diff --git a/src/program_builder.cc b/src/program_builder.cc
index bee3f29..57e8464 100644
--- a/src/program_builder.cc
+++ b/src/program_builder.cc
@@ -94,7 +94,7 @@
 }
 
 ast::Statement* ProgramBuilder::WrapInStatement(ast::Expression* expr) {
-  // TODO(ben-clayton): This is valid enough for the TypeDeterminer, but the LHS
+  // TODO(ben-clayton): This is valid enough for the Resolver, but the LHS
   // may not be assignable, and so may not validate.
   return create<ast::AssignmentStatement>(expr, expr);
 }
diff --git a/src/program_builder.h b/src/program_builder.h
index d7f0f2a..984e392 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -206,12 +206,11 @@
     return diagnostics_;
   }
 
-  /// Controls whether the TypeDeterminer will be run on the program when it is
-  /// built.
+  /// Controls whether the Resolver will be run on the program when it is built.
   /// @param enable the new flag value (defaults to true)
   void SetResolveOnBuild(bool enable) { resolve_on_build_ = enable; }
 
-  /// @return true if the TypeDeterminer will be run on the program when it is
+  /// @return true if the Resolver will be run on the program when it is
   /// built.
   bool ResolveOnBuild() const { return resolve_on_build_; }
 
@@ -1128,22 +1127,21 @@
   }
 
   /// Helper for returning the resolved semantic type of the expression `expr`.
-  /// @note As the TypeDeterminator is run when the Program is built, this will
-  /// only be useful for the TypeDeterminer itself and tests that use their own
-  /// TypeDeterminer.
+  /// @note As the Resolver is run when the Program is built, this will only be
+  /// useful for the Resolver itself and tests that use their own Resolver.
   /// @param expr the AST expression
   /// @return the resolved semantic type for the expression, or nullptr if the
   /// expression has no resolved type.
   type::Type* TypeOf(ast::Expression* expr) const;
 
   /// Wraps the ast::Expression in a statement. This is used by tests that
-  /// construct a partial AST and require the TypeDeterminer to reach these
+  /// construct a partial AST and require the Resolver to reach these
   /// nodes.
   /// @param expr the ast::Expression to be wrapped by an ast::Statement
   /// @return the ast::Statement that wraps the ast::Expression
   ast::Statement* WrapInStatement(ast::Expression* expr);
   /// Wraps the ast::Variable in a ast::VariableDeclStatement. This is used by
-  /// tests that construct a partial AST and require the TypeDeterminer to reach
+  /// tests that construct a partial AST and require the Resolver to reach
   /// these nodes.
   /// @param v the ast::Variable to be wrapped by an ast::VariableDeclStatement
   /// @return the ast::VariableDeclStatement that wraps the ast::Variable
@@ -1154,7 +1152,7 @@
   /// @return `stmt`
   ast::Statement* WrapInStatement(ast::Statement* stmt);
   /// Wraps the list of arguments in a simple function so that each is reachable
-  /// by the TypeDeterminer.
+  /// by the Resolver.
   /// @param args a mix of ast::Expression, ast::Statement, ast::Variables.
   template <typename... ARGS>
   void WrapInFunction(ARGS&&... args) {
@@ -1162,7 +1160,7 @@
     WrapInFunction(stmts);
   }
   /// @param stmts a list of ast::Statement that will be wrapped by a function,
-  /// so that each statement is reachable by the TypeDeterminer.
+  /// so that each statement is reachable by the Resolver.
   void WrapInFunction(ast::StatementList stmts);
 
   /// The builder types
@@ -1185,7 +1183,7 @@
   /// the first argument.
   Source source_;
 
-  /// Set by SetResolveOnBuild(). If set, the TypeDeterminer will be run on the
+  /// Set by SetResolveOnBuild(). If set, the Resolver will be run on the
   /// program when built.
   bool resolve_on_build_ = true;
 
diff --git a/src/reader/spirv/parser_impl_test_helper.h b/src/reader/spirv/parser_impl_test_helper.h
index fb4a2ff..e41bdbe 100644
--- a/src/reader/spirv/parser_impl_test_helper.h
+++ b/src/reader/spirv/parser_impl_test_helper.h
@@ -41,7 +41,7 @@
   /// @returns a parser for the given binary
   std::unique_ptr<ParserImpl> parser(const std::vector<uint32_t>& input) {
     auto parser = std::make_unique<ParserImpl>(input);
-    // Don't run the TypeDeterminer when building the program.
+    // Don't run the Resolver when building the program.
     // We're not interested in type information with these tests.
     parser->builder().SetResolveOnBuild(false);
     return parser;
diff --git a/src/type_determiner.cc b/src/resolver/resolver.cc
similarity index 83%
rename from src/type_determiner.cc
rename to src/resolver/resolver.cc
index 6f71979..c652bbd 100644
--- a/src/type_determiner.cc
+++ b/src/resolver/resolver.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/type_determiner.h"
+#include "src/resolver/resolver.h"
 
 #include <algorithm>
 #include <memory>
@@ -91,20 +91,20 @@
 
 }  // namespace
 
-TypeDeterminer::TypeDeterminer(ProgramBuilder* builder)
+Resolver::Resolver(ProgramBuilder* builder)
     : builder_(builder), intrinsic_table_(IntrinsicTable::Create()) {}
 
-TypeDeterminer::~TypeDeterminer() = default;
+Resolver::~Resolver() = default;
 
-TypeDeterminer::BlockInfo::BlockInfo(TypeDeterminer::BlockInfo::Type type,
-                                     TypeDeterminer::BlockInfo* parent,
-                                     const ast::BlockStatement* block)
-    : type(type), parent(parent), block(block) {}
+Resolver::BlockInfo::BlockInfo(Resolver::BlockInfo::Type ty,
+                               Resolver::BlockInfo* p,
+                               const ast::BlockStatement* b)
+    : type(ty), parent(p), block(b) {}
 
-TypeDeterminer::BlockInfo::~BlockInfo() = default;
+Resolver::BlockInfo::~BlockInfo() = default;
 
-void TypeDeterminer::set_referenced_from_function_if_needed(VariableInfo* var,
-                                                            bool local) {
+void Resolver::set_referenced_from_function_if_needed(VariableInfo* var,
+                                                      bool local) {
   if (current_function_ == nullptr) {
     return;
   }
@@ -119,8 +119,8 @@
   }
 }
 
-bool TypeDeterminer::Determine() {
-  bool result = DetermineInternal();
+bool Resolver::Resolve() {
+  bool result = ResolveInternal();
 
   // Even if resolving failed, create all the semantic nodes for information we
   // did generate.
@@ -129,34 +129,34 @@
   return result;
 }
 
-bool TypeDeterminer::DetermineInternal() {
+bool Resolver::ResolveInternal() {
   for (auto* var : builder_->AST().GlobalVariables()) {
     variable_stack_.set_global(var->symbol(), CreateVariableInfo(var));
 
     if (var->has_constructor()) {
-      if (!DetermineResultType(var->constructor())) {
+      if (!Expression(var->constructor())) {
         return false;
       }
     }
   }
 
-  if (!DetermineFunctions(builder_->AST().Functions())) {
+  if (!Functions(builder_->AST().Functions())) {
     return false;
   }
 
   return true;
 }
 
-bool TypeDeterminer::DetermineFunctions(const ast::FunctionList& funcs) {
+bool Resolver::Functions(const ast::FunctionList& funcs) {
   for (auto* func : funcs) {
-    if (!DetermineFunction(func)) {
+    if (!Function(func)) {
       return false;
     }
   }
   return true;
 }
 
-bool TypeDeterminer::DetermineFunction(ast::Function* func) {
+bool Resolver::Function(ast::Function* func) {
   auto* func_info = function_infos_.Create<FunctionInfo>(func);
 
   ScopedAssignment<FunctionInfo*> sa(current_function_, func_info);
@@ -166,7 +166,7 @@
     variable_stack_.set(param->symbol(), CreateVariableInfo(param));
   }
 
-  if (!DetermineBlockStatement(func->body())) {
+  if (!BlockStatement(func->body())) {
     return false;
   }
   variable_stack_.pop_scope();
@@ -180,35 +180,35 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineBlockStatement(const ast::BlockStatement* stmt) {
+bool Resolver::BlockStatement(const ast::BlockStatement* stmt) {
   auto* block =
       block_infos_.Create(BlockInfo::Type::Generic, current_block_, stmt);
   block_to_info_[stmt] = block;
   ScopedAssignment<BlockInfo*> scope_sa(current_block_, block);
 
-  return DetermineStatements(stmt->list());
+  return Statements(stmt->list());
 }
 
-bool TypeDeterminer::DetermineStatements(const ast::StatementList& stmts) {
+bool Resolver::Statements(const ast::StatementList& stmts) {
   for (auto* stmt : stmts) {
     if (auto* decl = stmt->As<ast::VariableDeclStatement>()) {
-      if (!ValidateVariableDeclStatement(decl)) {
+      if (!VariableDeclStatement(decl)) {
         return false;
       }
     }
 
-    if (!DetermineVariableStorageClass(stmt)) {
+    if (!VariableStorageClass(stmt)) {
       return false;
     }
 
-    if (!DetermineResultType(stmt)) {
+    if (!Statement(stmt)) {
       return false;
     }
   }
   return true;
 }
 
-bool TypeDeterminer::DetermineVariableStorageClass(ast::Statement* stmt) {
+bool Resolver::VariableStorageClass(ast::Statement* stmt) {
   auto* var_decl = stmt->As<ast::VariableDeclStatement>();
   if (var_decl == nullptr) {
     return true;
@@ -238,25 +238,25 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineResultType(ast::Statement* stmt) {
+bool Resolver::Statement(ast::Statement* stmt) {
   auto* sem_statement = builder_->create<semantic::Statement>(stmt);
 
   ScopedAssignment<semantic::Statement*> sa(current_statement_, sem_statement);
 
   if (auto* a = stmt->As<ast::AssignmentStatement>()) {
-    return DetermineResultType(a->lhs()) && DetermineResultType(a->rhs());
+    return Expression(a->lhs()) && Expression(a->rhs());
   }
   if (auto* b = stmt->As<ast::BlockStatement>()) {
-    return DetermineBlockStatement(b);
+    return BlockStatement(b);
   }
   if (stmt->Is<ast::BreakStatement>()) {
     return true;
   }
   if (auto* c = stmt->As<ast::CallStatement>()) {
-    return DetermineResultType(c->expr());
+    return Expression(c->expr());
   }
   if (auto* c = stmt->As<ast::CaseStatement>()) {
-    return DetermineBlockStatement(c->body());
+    return BlockStatement(c->body());
   }
   if (stmt->Is<ast::ContinueStatement>()) {
     // Set if we've hit the first continue statement in our parent loop
@@ -277,20 +277,18 @@
     return true;
   }
   if (auto* e = stmt->As<ast::ElseStatement>()) {
-    return DetermineResultType(e->condition()) &&
-           DetermineBlockStatement(e->body());
+    return Expression(e->condition()) && BlockStatement(e->body());
   }
   if (stmt->Is<ast::FallthroughStatement>()) {
     return true;
   }
   if (auto* i = stmt->As<ast::IfStatement>()) {
-    if (!DetermineResultType(i->condition()) ||
-        !DetermineBlockStatement(i->body())) {
+    if (!Expression(i->condition()) || !BlockStatement(i->body())) {
       return false;
     }
 
     for (auto* else_stmt : i->else_statements()) {
-      if (!DetermineResultType(else_stmt)) {
+      if (!Statement(else_stmt)) {
         return false;
       }
     }
@@ -306,31 +304,31 @@
     block_to_info_[l->body()] = block;
     ScopedAssignment<BlockInfo*> scope_sa(current_block_, block);
 
-    if (!DetermineStatements(l->body()->list())) {
+    if (!Statements(l->body()->list())) {
       return false;
     }
 
     if (l->has_continuing()) {
-      auto* block = block_infos_.Create(BlockInfo::Type::LoopContinuing,
-                                        current_block_, l->continuing());
-      block_to_info_[l->continuing()] = block;
-      ScopedAssignment<BlockInfo*> scope_sa(current_block_, block);
+      auto* cont_block = block_infos_.Create(BlockInfo::Type::LoopContinuing,
+                                             current_block_, l->continuing());
+      block_to_info_[l->continuing()] = cont_block;
+      ScopedAssignment<BlockInfo*> scope_sa2(current_block_, cont_block);
 
-      if (!DetermineStatements(l->continuing()->list())) {
+      if (!Statements(l->continuing()->list())) {
         return false;
       }
     }
     return true;
   }
   if (auto* r = stmt->As<ast::ReturnStatement>()) {
-    return DetermineResultType(r->value());
+    return Expression(r->value());
   }
   if (auto* s = stmt->As<ast::SwitchStatement>()) {
-    if (!DetermineResultType(s->condition())) {
+    if (!Expression(s->condition())) {
       return false;
     }
     for (auto* case_stmt : s->body()) {
-      if (!DetermineResultType(case_stmt)) {
+      if (!Statement(case_stmt)) {
         return false;
       }
     }
@@ -340,7 +338,7 @@
     variable_stack_.set(v->variable()->symbol(),
                         variable_to_info_.at(v->variable()));
     current_block_->decls.push_back(v->variable());
-    return DetermineResultType(v->variable()->constructor());
+    return Expression(v->variable()->constructor());
   }
 
   diagnostics_.add_error(
@@ -349,16 +347,16 @@
   return false;
 }
 
-bool TypeDeterminer::DetermineResultType(const ast::ExpressionList& list) {
+bool Resolver::Expressions(const ast::ExpressionList& list) {
   for (auto* expr : list) {
-    if (!DetermineResultType(expr)) {
+    if (!Expression(expr)) {
       return false;
     }
   }
   return true;
 }
 
-bool TypeDeterminer::DetermineResultType(ast::Expression* expr) {
+bool Resolver::Expression(ast::Expression* expr) {
   // This is blindly called above, so in some cases the expression won't exist.
   if (!expr) {
     return true;
@@ -369,28 +367,28 @@
   }
 
   if (auto* a = expr->As<ast::ArrayAccessorExpression>()) {
-    return DetermineArrayAccessor(a);
+    return ArrayAccessor(a);
   }
   if (auto* b = expr->As<ast::BinaryExpression>()) {
-    return DetermineBinary(b);
+    return Binary(b);
   }
   if (auto* b = expr->As<ast::BitcastExpression>()) {
-    return DetermineBitcast(b);
+    return Bitcast(b);
   }
   if (auto* c = expr->As<ast::CallExpression>()) {
-    return DetermineCall(c);
+    return Call(c);
   }
   if (auto* c = expr->As<ast::ConstructorExpression>()) {
-    return DetermineConstructor(c);
+    return Constructor(c);
   }
   if (auto* i = expr->As<ast::IdentifierExpression>()) {
-    return DetermineIdentifier(i);
+    return Identifier(i);
   }
   if (auto* m = expr->As<ast::MemberAccessorExpression>()) {
-    return DetermineMemberAccessor(m);
+    return MemberAccessor(m);
   }
   if (auto* u = expr->As<ast::UnaryOpExpression>()) {
-    return DetermineUnaryOp(u);
+    return UnaryOp(u);
   }
 
   diagnostics_.add_error("unknown expression for type determination",
@@ -398,12 +396,11 @@
   return false;
 }
 
-bool TypeDeterminer::DetermineArrayAccessor(
-    ast::ArrayAccessorExpression* expr) {
-  if (!DetermineResultType(expr->array())) {
+bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
+  if (!Expression(expr->array())) {
     return false;
   }
-  if (!DetermineResultType(expr->idx_expr())) {
+  if (!Expression(expr->idx_expr())) {
     return false;
   }
 
@@ -439,16 +436,16 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineBitcast(ast::BitcastExpression* expr) {
-  if (!DetermineResultType(expr->expr())) {
+bool Resolver::Bitcast(ast::BitcastExpression* expr) {
+  if (!Expression(expr->expr())) {
     return false;
   }
   SetType(expr, expr->type());
   return true;
 }
 
-bool TypeDeterminer::DetermineCall(ast::CallExpression* call) {
-  if (!DetermineResultType(call->params())) {
+bool Resolver::Call(ast::CallExpression* call) {
+  if (!Expressions(call->params())) {
     return false;
   }
 
@@ -465,7 +462,7 @@
 
   auto intrinsic_type = semantic::ParseIntrinsicType(name);
   if (intrinsic_type != IntrinsicType::kNone) {
-    if (!DetermineIntrinsicCall(call, intrinsic_type)) {
+    if (!IntrinsicCall(call, intrinsic_type)) {
       return false;
     }
   } else {
@@ -515,9 +512,8 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineIntrinsicCall(
-    ast::CallExpression* call,
-    semantic::IntrinsicType intrinsic_type) {
+bool Resolver::IntrinsicCall(ast::CallExpression* call,
+                             semantic::IntrinsicType intrinsic_type) {
   std::vector<type::Type*> arg_tys;
   arg_tys.reserve(call->params().size());
   for (auto* expr : call->params()) {
@@ -571,10 +567,10 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineConstructor(ast::ConstructorExpression* expr) {
+bool Resolver::Constructor(ast::ConstructorExpression* expr) {
   if (auto* ty = expr->As<ast::TypeConstructorExpression>()) {
     for (auto* value : ty->values()) {
-      if (!DetermineResultType(value)) {
+      if (!Expression(value)) {
         return false;
       }
     }
@@ -586,7 +582,7 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineIdentifier(ast::IdentifierExpression* expr) {
+bool Resolver::Identifier(ast::IdentifierExpression* expr) {
   auto symbol = expr->symbol();
   VariableInfo* var;
   if (variable_stack_.get(symbol, &var)) {
@@ -615,9 +611,9 @@
         auto& decls = loop_block->decls;
         // If our identifier is in loop_block->decls, make sure its index is
         // less than first_continue
-        auto iter = std::find_if(
-            decls.begin(), decls.end(),
-            [&symbol](auto* var) { return var->symbol() == symbol; });
+        auto iter =
+            std::find_if(decls.begin(), decls.end(),
+                         [&symbol](auto* v) { return v->symbol() == symbol; });
         if (iter != decls.end()) {
           auto var_decl_index =
               static_cast<size_t>(std::distance(decls.begin(), iter));
@@ -656,9 +652,8 @@
   return false;
 }
 
-bool TypeDeterminer::DetermineMemberAccessor(
-    ast::MemberAccessorExpression* expr) {
-  if (!DetermineResultType(expr->structure())) {
+bool Resolver::MemberAccessor(ast::MemberAccessorExpression* expr) {
+  if (!Expression(expr->structure())) {
     return false;
   }
 
@@ -772,8 +767,8 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineBinary(ast::BinaryExpression* expr) {
-  if (!DetermineResultType(expr->lhs()) || !DetermineResultType(expr->rhs())) {
+bool Resolver::Binary(ast::BinaryExpression* expr) {
+  if (!Expression(expr->lhs()) || !Expression(expr->rhs())) {
     return false;
   }
 
@@ -844,9 +839,9 @@
   return false;
 }
 
-bool TypeDeterminer::DetermineUnaryOp(ast::UnaryOpExpression* expr) {
+bool Resolver::UnaryOp(ast::UnaryOpExpression* expr) {
   // Result type matches the parameter type.
-  if (!DetermineResultType(expr->expr())) {
+  if (!Expression(expr->expr())) {
     return false;
   }
 
@@ -855,8 +850,7 @@
   return true;
 }
 
-bool TypeDeterminer::ValidateVariableDeclStatement(
-    const ast::VariableDeclStatement* stmt) {
+bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
   auto* ctor = stmt->variable()->constructor();
   if (!ctor) {
     return true;
@@ -877,14 +871,13 @@
   return true;
 }
 
-TypeDeterminer::VariableInfo* TypeDeterminer::CreateVariableInfo(
-    ast::Variable* var) {
+Resolver::VariableInfo* Resolver::CreateVariableInfo(ast::Variable* var) {
   auto* info = variable_infos_.Create(var);
   variable_to_info_.emplace(var, info);
   return info;
 }
 
-type::Type* TypeDeterminer::TypeOf(ast::Expression* expr) {
+type::Type* Resolver::TypeOf(ast::Expression* expr) {
   auto it = expr_info_.find(expr);
   if (it != expr_info_.end()) {
     return it->second.type;
@@ -892,12 +885,12 @@
   return nullptr;
 }
 
-void TypeDeterminer::SetType(ast::Expression* expr, type::Type* type) {
+void Resolver::SetType(ast::Expression* expr, type::Type* type) {
   assert(expr_info_.count(expr) == 0);
   expr_info_.emplace(expr, ExpressionInfo{type, current_statement_});
 }
 
-void TypeDeterminer::CreateSemanticNodes() const {
+void Resolver::CreateSemanticNodes() const {
   auto& sem = builder_->Sem();
 
   // Collate all the 'ancestor_entry_points' - this is a map of function symbol
@@ -906,7 +899,7 @@
   for (auto* func : builder_->AST().Functions()) {
     auto it = function_to_info_.find(func);
     if (it == function_to_info_.end()) {
-      continue;  // Type determination has likely errored. Process what we can.
+      continue;  // Resolver has likely errored. Process what we can.
     }
 
     auto* info = it->second;
@@ -984,14 +977,13 @@
   }
 }
 
-TypeDeterminer::VariableInfo::VariableInfo(ast::Variable* decl)
+Resolver::VariableInfo::VariableInfo(ast::Variable* decl)
     : declaration(decl), storage_class(decl->declared_storage_class()) {}
 
-TypeDeterminer::VariableInfo::~VariableInfo() = default;
+Resolver::VariableInfo::~VariableInfo() = default;
 
-TypeDeterminer::FunctionInfo::FunctionInfo(ast::Function* decl)
-    : declaration(decl) {}
+Resolver::FunctionInfo::FunctionInfo(ast::Function* decl) : declaration(decl) {}
 
-TypeDeterminer::FunctionInfo::~FunctionInfo() = default;
+Resolver::FunctionInfo::~FunctionInfo() = default;
 
 }  // namespace tint
diff --git a/src/type_determiner.h b/src/resolver/resolver.h
similarity index 70%
rename from src/type_determiner.h
rename to src/resolver/resolver.h
index b32f9e3..2099c41 100644
--- a/src/type_determiner.h
+++ b/src/resolver/resolver.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SRC_TYPE_DETERMINER_H_
-#define SRC_TYPE_DETERMINER_H_
+#ifndef SRC_RESOLVER_RESOLVER_H_
+#define SRC_RESOLVER_RESOLVER_H_
 
 #include <memory>
 #include <string>
@@ -49,21 +49,21 @@
 class Statement;
 }  // namespace semantic
 
-/// Determines types for all items in the given tint program
-class TypeDeterminer {
+/// Resolves types for all items in the given tint program
+class Resolver {
  public:
   /// Constructor
   /// @param builder the program builder
-  explicit TypeDeterminer(ProgramBuilder* builder);
+  explicit Resolver(ProgramBuilder* builder);
 
   /// Destructor
-  ~TypeDeterminer();
+  ~Resolver();
 
-  /// @returns error messages from the type determiner
+  /// @returns error messages from the resolver
   std::string error() const { return diagnostics_.str(); }
 
-  /// @returns true if the type determiner was successful
-  bool Determine();
+  /// @returns true if the resolver was successful
+  bool Resolve();
 
  private:
   /// Structure holding semantic information about a variable.
@@ -124,9 +124,9 @@
       return curr;
     }
 
-    BlockInfo* FindFirstParent(BlockInfo::Type type) {
+    BlockInfo* FindFirstParent(BlockInfo::Type ty) {
       return FindFirstParent(
-          [type](auto* block_info) { return block_info->type == type; });
+          [ty](auto* block_info) { return block_info->type == ty; });
     }
 
     const Type type;
@@ -143,45 +143,44 @@
   BlockAllocator<BlockInfo> block_infos_;
   BlockInfo* current_block_ = nullptr;
 
-  /// Determines type information for the program, without creating final the
-  /// semantic nodes.
-  /// @returns true if the determination was successful
-  bool DetermineInternal();
+  /// Resolves the program, without creating final the semantic nodes.
+  /// @returns true on success, false on error
+  bool ResolveInternal();
 
-  /// Determines type information for functions
+  /// Resolves functions
   /// @param funcs the functions to check
-  /// @returns true if the determination was successful
-  bool DetermineFunctions(const ast::FunctionList& funcs);
-  /// Determines type information for a function. Requires all dependency
+  /// @returns true on success, false on error
+  bool Functions(const ast::FunctionList& funcs);
+  /// Resolves a function. Requires all dependency
   /// (callee) functions to have DetermineFunction() called on them first.
   /// @param func the function to check
-  /// @returns true if the determination was successful
-  bool DetermineFunction(ast::Function* func);
-  /// Determines the type information for a block statement
+  /// @returns true on success, false on error
+  bool Function(ast::Function* func);
+  /// Resolves a block statement
   /// @param stmt the block statement
   /// @returns true if determination was successful
-  bool DetermineBlockStatement(const ast::BlockStatement* stmt);
-  /// Determines type information for a set of statements
-  /// @param stmts the statements to check
-  /// @returns true if the determination was successful
-  bool DetermineStatements(const ast::StatementList& stmts);
-  /// Determines type information for a statement
+  bool BlockStatement(const ast::BlockStatement* stmt);
+  /// Resolves the list of statements
+  /// @param stmts the statements to resolve
+  /// @returns true on success, false on error
+  bool Statements(const ast::StatementList& stmts);
+  /// Resolves a statement
   /// @param stmt the statement to check
-  /// @returns true if the determination was successful
-  bool DetermineResultType(ast::Statement* stmt);
-  /// Determines type information for an expression list
+  /// @returns true on success, false on error
+  bool Statement(ast::Statement* stmt);
+  /// Resolves an expression list
   /// @param list the expression list to check
-  /// @returns true if the determination was successful
-  bool DetermineResultType(const ast::ExpressionList& list);
-  /// Determines type information for an expression
+  /// @returns true on success, false on error
+  bool Expressions(const ast::ExpressionList& list);
+  /// Resolves an expression
   /// @param expr the expression to check
-  /// @returns true if the determination was successful
-  bool DetermineResultType(ast::Expression* expr);
-  /// Determines the storage class for variables. This assumes that it is only
+  /// @returns true on success, false on error
+  bool Expression(ast::Expression* expr);
+  /// Resolves the storage class for variables. This assumes that it is only
   /// called for things in function scope, not module scope.
   /// @param stmt the statement to check
   /// @returns false on error
-  bool DetermineVariableStorageClass(ast::Statement* stmt);
+  bool VariableStorageClass(ast::Statement* stmt);
 
   /// Creates the nodes and adds them to the semantic::Info mappings of the
   /// ProgramBuilder.
@@ -202,18 +201,18 @@
 
   void set_referenced_from_function_if_needed(VariableInfo* var, bool local);
 
-  bool DetermineArrayAccessor(ast::ArrayAccessorExpression* expr);
-  bool DetermineBinary(ast::BinaryExpression* expr);
-  bool DetermineBitcast(ast::BitcastExpression* expr);
-  bool DetermineCall(ast::CallExpression* expr);
-  bool DetermineConstructor(ast::ConstructorExpression* expr);
-  bool DetermineIdentifier(ast::IdentifierExpression* expr);
-  bool DetermineIntrinsicCall(ast::CallExpression* call,
-                              semantic::IntrinsicType intrinsic_type);
-  bool DetermineMemberAccessor(ast::MemberAccessorExpression* expr);
-  bool DetermineUnaryOp(ast::UnaryOpExpression* expr);
+  bool ArrayAccessor(ast::ArrayAccessorExpression* expr);
+  bool Binary(ast::BinaryExpression* expr);
+  bool Bitcast(ast::BitcastExpression* expr);
+  bool Call(ast::CallExpression* expr);
+  bool Constructor(ast::ConstructorExpression* expr);
+  bool Identifier(ast::IdentifierExpression* expr);
+  bool IntrinsicCall(ast::CallExpression* call,
+                     semantic::IntrinsicType intrinsic_type);
+  bool MemberAccessor(ast::MemberAccessorExpression* expr);
+  bool UnaryOp(ast::UnaryOpExpression* expr);
 
-  bool ValidateVariableDeclStatement(const ast::VariableDeclStatement* stmt);
+  bool VariableDeclStatement(const ast::VariableDeclStatement* stmt);
 
   VariableInfo* CreateVariableInfo(ast::Variable*);
 
@@ -244,4 +243,4 @@
 
 }  // namespace tint
 
-#endif  // SRC_TYPE_DETERMINER_H_
+#endif  // SRC_RESOLVER_RESOLVER_H_
diff --git a/src/type_determiner_test.cc b/src/resolver/resolver_test.cc
similarity index 83%
rename from src/type_determiner_test.cc
rename to src/resolver/resolver_test.cc
index dd66bbf..ab45029 100644
--- a/src/type_determiner_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/type_determiner.h"
+#include "src/resolver/resolver.h"
 
 #include <algorithm>
 #include <memory>
@@ -102,11 +102,11 @@
   void to_str(const semantic::Info&, std::ostream&, size_t) const override {}
 };
 
-class TypeDeterminerHelper : public ProgramBuilder {
+class ResolverHelper : public ProgramBuilder {
  public:
-  TypeDeterminerHelper() : td_(std::make_unique<TypeDeterminer>(this)) {}
+  ResolverHelper() : td_(std::make_unique<Resolver>(this)) {}
 
-  TypeDeterminer* td() const { return td_.get(); }
+  Resolver* r() const { return td_.get(); }
 
   ast::Statement* StmtOf(ast::Expression* expr) {
     auto* sem_stmt = Sem().Get(expr)->Stmt();
@@ -128,43 +128,43 @@
   }
 
  private:
-  std::unique_ptr<TypeDeterminer> td_;
+  std::unique_ptr<Resolver> td_;
 };
 
-class TypeDeterminerTest : public TypeDeterminerHelper, public testing::Test {};
+class ResolverTest : public ResolverHelper, public testing::Test {};
 
 template <typename T>
-class TypeDeterminerTestWithParam : public TypeDeterminerHelper,
-                                    public testing::TestWithParam<T> {};
+class ResolverTestWithParam : public ResolverHelper,
+                              public testing::TestWithParam<T> {};
 
-TEST_F(TypeDeterminerTest, Error_WithEmptySource) {
+TEST_F(ResolverTest, Error_WithEmptySource) {
   auto* s = create<FakeStmt>();
   WrapInFunction(s);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: unknown statement type for type determination: Fake");
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Error_Unknown) {
+TEST_F(ResolverTest, Stmt_Error_Unknown) {
   auto* s = create<FakeStmt>(Source{Source::Location{2, 30}});
   WrapInFunction(s);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "2:30 error: unknown statement type for type determination: Fake");
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Assign) {
+TEST_F(ResolverTest, Stmt_Assign) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
   auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
   WrapInFunction(assign);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
@@ -175,7 +175,7 @@
   EXPECT_EQ(StmtOf(rhs), assign);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Case) {
+TEST_F(ResolverTest, Stmt_Case) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
@@ -188,7 +188,7 @@
   auto* cse = create<ast::CaseStatement>(lit, body);
   WrapInFunction(cse);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
@@ -198,7 +198,7 @@
   EXPECT_EQ(StmtOf(rhs), assign);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Block) {
+TEST_F(ResolverTest, Stmt_Block) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
@@ -208,7 +208,7 @@
   });
   WrapInFunction(block);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(lhs), nullptr);
   ASSERT_NE(TypeOf(rhs), nullptr);
@@ -218,7 +218,7 @@
   EXPECT_EQ(StmtOf(rhs), assign);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Else) {
+TEST_F(ResolverTest, Stmt_Else) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
@@ -230,7 +230,7 @@
   auto* stmt = create<ast::ElseStatement>(cond, body);
   WrapInFunction(stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(stmt->condition()), nullptr);
   ASSERT_NE(TypeOf(lhs), nullptr);
@@ -243,7 +243,7 @@
   EXPECT_EQ(StmtOf(cond), stmt);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_If) {
+TEST_F(ResolverTest, Stmt_If) {
   auto* else_lhs = Expr(2);
   auto* else_rhs = Expr(2.3f);
 
@@ -264,7 +264,7 @@
       create<ast::IfStatement>(cond, body, ast::ElseStatementList{else_stmt});
   WrapInFunction(stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(stmt->condition()), nullptr);
   ASSERT_NE(TypeOf(else_lhs), nullptr);
@@ -282,7 +282,7 @@
   EXPECT_EQ(StmtOf(else_cond), else_stmt);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Loop) {
+TEST_F(ResolverTest, Stmt_Loop) {
   auto* body_lhs = Expr(2);
   auto* body_rhs = Expr(2.3f);
 
@@ -300,7 +300,7 @@
   auto* stmt = create<ast::LoopStatement>(body, continuing);
   WrapInFunction(stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(body_lhs), nullptr);
   ASSERT_NE(TypeOf(body_rhs), nullptr);
@@ -312,8 +312,7 @@
   EXPECT_TRUE(TypeOf(continuing_rhs)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest,
-       Stmt_Loop_ContinueInLoopBodyBeforeDecl_UsageInContinuing) {
+TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyBeforeDecl_UsageInContinuing) {
   // loop  {
   //     continue; // Bypasses z decl
   //     var z : i32;
@@ -330,13 +329,13 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_FALSE(td()->Determine()) << td()->error();
-  EXPECT_EQ(td()->error(),
+  EXPECT_FALSE(r()->Resolve()) << r()->error();
+  EXPECT_EQ(r()->error(),
             "12:34 error: continue statement bypasses declaration of 'z' in "
             "continuing block");
 }
 
-TEST_F(TypeDeterminerTest,
+TEST_F(ResolverTest,
        Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing) {
   // loop  {
   //     continue; // Bypasses z decl
@@ -356,13 +355,13 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_FALSE(td()->Determine()) << td()->error();
-  EXPECT_EQ(td()->error(),
+  EXPECT_FALSE(r()->Resolve()) << r()->error();
+  EXPECT_EQ(r()->error(),
             "12:34 error: continue statement bypasses declaration of 'z' in "
             "continuing block");
 }
 
-TEST_F(TypeDeterminerTest,
+TEST_F(ResolverTest,
        Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuing) {
   // loop  {
   //     if (true) {
@@ -381,14 +380,14 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_FALSE(td()->Determine()) << td()->error();
-  EXPECT_EQ(td()->error(),
+  EXPECT_FALSE(r()->Resolve()) << r()->error();
+  EXPECT_EQ(r()->error(),
             "12:34 error: continue statement bypasses declaration of 'z' in "
             "continuing block");
 }
 
 TEST_F(
-    TypeDeterminerTest,
+    ResolverTest,
     Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingSubscope) {
   // loop  {
   //     if (true) {
@@ -411,13 +410,13 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_FALSE(td()->Determine()) << td()->error();
-  EXPECT_EQ(td()->error(),
+  EXPECT_FALSE(r()->Resolve()) << r()->error();
+  EXPECT_EQ(r()->error(),
             "12:34 error: continue statement bypasses declaration of 'z' in "
             "continuing block");
 }
 
-TEST_F(TypeDeterminerTest,
+TEST_F(ResolverTest,
        Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingLoop) {
   // loop  {
   //     if (true) {
@@ -439,13 +438,13 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_FALSE(td()->Determine()) << td()->error();
-  EXPECT_EQ(td()->error(),
+  EXPECT_FALSE(r()->Resolve()) << r()->error();
+  EXPECT_EQ(r()->error(),
             "12:34 error: continue statement bypasses declaration of 'z' in "
             "continuing block");
 }
 
-TEST_F(TypeDeterminerTest,
+TEST_F(ResolverTest,
        Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
   // loop  {
   //     loop {
@@ -465,10 +464,10 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(TypeDeterminerTest,
+TEST_F(ResolverTest,
        Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingSubscope) {
   // loop  {
   //     loop {
@@ -490,10 +489,10 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(TypeDeterminerTest,
+TEST_F(ResolverTest,
        Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
   // loop  {
   //     loop {
@@ -515,35 +514,35 @@
   auto* loop_stmt = Loop(body, continuing);
   WrapInFunction(loop_stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(TypeDeterminerTest, Stmt_ContinueNotInLoop) {
+TEST_F(ResolverTest, Stmt_ContinueNotInLoop) {
   WrapInFunction(create<ast::ContinueStatement>());
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(), "error: continue statement must be in a loop");
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(), "error: continue statement must be in a loop");
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Return) {
+TEST_F(ResolverTest, Stmt_Return) {
   auto* cond = Expr(2);
 
   auto* ret = create<ast::ReturnStatement>(cond);
   WrapInFunction(ret);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(cond), nullptr);
   EXPECT_TRUE(TypeOf(cond)->Is<type::I32>());
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Return_WithoutValue) {
+TEST_F(ResolverTest, Stmt_Return_WithoutValue) {
   auto* ret = create<ast::ReturnStatement>();
   WrapInFunction(ret);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Switch) {
+TEST_F(ResolverTest, Stmt_Switch) {
   auto* lhs = Expr(2);
   auto* rhs = Expr(2.3f);
 
@@ -559,7 +558,7 @@
   auto* stmt = create<ast::SwitchStatement>(Expr(2), cases);
   WrapInFunction(stmt);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(stmt->condition()), nullptr);
   ASSERT_NE(TypeOf(lhs), nullptr);
@@ -570,7 +569,7 @@
   EXPECT_TRUE(TypeOf(rhs)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Call) {
+TEST_F(ResolverTest, Stmt_Call) {
   ast::VariableList params;
   Func("my_func", params, ty.f32(), ast::StatementList{},
        ast::FunctionDecorationList{});
@@ -580,14 +579,14 @@
   auto* call = create<ast::CallStatement>(expr);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
   EXPECT_EQ(StmtOf(expr), call);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Call_undeclared) {
+TEST_F(ResolverTest, Stmt_Call_undeclared) {
   // fn main() -> void {func(); return; }
   // fn func() -> void { return; }
 
@@ -608,13 +607,13 @@
        },
        ast::FunctionDecorationList{});
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "12:34 error: v-0006: unable to find called function: func");
 }
 
-TEST_F(TypeDeterminerTest, Stmt_Call_recursive) {
+TEST_F(ResolverTest, Stmt_Call_recursive) {
   // fn main() -> void {main(); }
 
   SetSource(Source::Location{12, 34});
@@ -629,27 +628,27 @@
            create<ast::StageDecoration>(ast::PipelineStage::kVertex),
        });
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "12:34 error: recursion is not permitted. 'main' attempted to call "
             "itself.");
 }
 
-TEST_F(TypeDeterminerTest, Stmt_VariableDecl) {
+TEST_F(ResolverTest, Stmt_VariableDecl) {
   auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
   auto* init = var->constructor();
 
   auto* decl = create<ast::VariableDeclStatement>(var);
   WrapInFunction(decl);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(init), nullptr);
   EXPECT_TRUE(TypeOf(init)->Is<type::I32>());
 }
 
-TEST_F(TypeDeterminerTest, Stmt_VariableDecl_Alias) {
+TEST_F(ResolverTest, Stmt_VariableDecl_Alias) {
   auto* my_int = ty.alias("MyInt", ty.i32());
   auto* var = Var("my_var", my_int, ast::StorageClass::kNone, Expr(2));
   auto* init = var->constructor();
@@ -657,13 +656,13 @@
   auto* decl = create<ast::VariableDeclStatement>(var);
   WrapInFunction(decl);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(init), nullptr);
   EXPECT_TRUE(TypeOf(init)->Is<type::I32>());
 }
 
-TEST_F(TypeDeterminerTest, Stmt_VariableDecl_MismatchedTypeScalarConstructor) {
+TEST_F(ResolverTest, Stmt_VariableDecl_MismatchedTypeScalarConstructor) {
   u32 unsigned_value = 2u;  // Type does not match variable type
   auto* var =
       Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(unsigned_value));
@@ -672,14 +671,13 @@
       create<ast::VariableDeclStatement>(Source{{{3, 3}, {3, 22}}}, var);
   WrapInFunction(decl);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(
-      td()->error(),
+      r()->error(),
       R"(3:3 error: constructor expression type does not match variable type)");
 }
 
-TEST_F(TypeDeterminerTest,
-       Stmt_VariableDecl_MismatchedTypeScalarConstructor_Alias) {
+TEST_F(ResolverTest, Stmt_VariableDecl_MismatchedTypeScalarConstructor_Alias) {
   auto* my_int = ty.alias("MyInt", ty.i32());
   u32 unsigned_value = 2u;  // Type does not match variable type
   auto* var =
@@ -689,24 +687,24 @@
       create<ast::VariableDeclStatement>(Source{{{3, 3}, {3, 22}}}, var);
   WrapInFunction(decl);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(
-      td()->error(),
+      r()->error(),
       R"(3:3 error: constructor expression type does not match variable type)");
 }
 
-TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScope) {
+TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
   auto* init = Expr(2);
   Global("my_var", ty.i32(), ast::StorageClass::kNone, init);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(init), nullptr);
   EXPECT_TRUE(TypeOf(init)->Is<type::I32>());
   EXPECT_EQ(StmtOf(init), nullptr);
 }
 
-TEST_F(TypeDeterminerTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
+TEST_F(ResolverTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
   // fn func_i32() -> i32 {
   //   {
   //     var foo : i32 = 2;
@@ -745,7 +743,7 @@
        ast::StatementList{inner, foo_f32_decl, bar_f32_decl},
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine());
+  EXPECT_TRUE(r()->Resolve());
   ASSERT_NE(TypeOf(foo_i32_init), nullptr);
   EXPECT_TRUE(TypeOf(foo_i32_init)->Is<type::I32>());
   ASSERT_NE(TypeOf(foo_f32_init), nullptr);
@@ -762,7 +760,7 @@
   EXPECT_TRUE(CheckVarUsers(foo_f32, {bar_f32->constructor()}));
 }
 
-TEST_F(TypeDeterminerTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
+TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
   // fn func_i32() -> i32 {
   //   var foo : i32 = 2;
   // }
@@ -792,7 +790,7 @@
   Func("func_f32", params, ty.f32(), ast::StatementList{fn_f32_decl},
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine());
+  EXPECT_TRUE(r()->Resolve());
   ASSERT_NE(TypeOf(mod_init), nullptr);
   EXPECT_TRUE(TypeOf(mod_init)->Is<type::F32>());
   ASSERT_NE(TypeOf(fn_i32_init), nullptr);
@@ -806,24 +804,24 @@
   EXPECT_TRUE(CheckVarUsers(mod_f32, {fn_f32->constructor()}));
 }
 
-TEST_F(TypeDeterminerTest, Expr_Error_Unknown) {
+TEST_F(ResolverTest, Expr_Error_Unknown) {
   FakeExpr e(Source{Source::Location{2, 30}});
   WrapInFunction(&e);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "2:30 error: unknown expression for type determination");
 }
 
-TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Array) {
+TEST_F(ResolverTest, Expr_ArrayAccessor_Array) {
   auto* idx = Expr(2);
   Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kFunction);
 
   auto* acc = IndexAccessor("my_var", idx);
   WrapInFunction(acc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(acc), nullptr);
   ASSERT_TRUE(TypeOf(acc)->Is<type::Pointer>());
@@ -832,7 +830,7 @@
   EXPECT_TRUE(ptr->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Alias_Array) {
+TEST_F(ResolverTest, Expr_ArrayAccessor_Alias_Array) {
   auto* aary = ty.alias("myarrty", ty.array<f32, 3>());
 
   Global("my_var", aary, ast::StorageClass::kFunction);
@@ -840,7 +838,7 @@
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(acc), nullptr);
   ASSERT_TRUE(TypeOf(acc)->Is<type::Pointer>());
@@ -849,25 +847,25 @@
   EXPECT_TRUE(ptr->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Array_Constant) {
+TEST_F(ResolverTest, Expr_ArrayAccessor_Array_Constant) {
   GlobalConst("my_var", ty.array<f32, 3>());
 
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(acc), nullptr);
   EXPECT_TRUE(TypeOf(acc)->Is<type::F32>()) << TypeOf(acc)->type_name();
 }
 
-TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Matrix) {
+TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix) {
   Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kNone);
 
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(acc), nullptr);
   ASSERT_TRUE(TypeOf(acc)->Is<type::Pointer>());
@@ -877,13 +875,13 @@
   EXPECT_EQ(ptr->type()->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Matrix_BothDimensions) {
+TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix_BothDimensions) {
   Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kNone);
 
   auto* acc = IndexAccessor(IndexAccessor("my_var", 2), 1);
   WrapInFunction(acc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(acc), nullptr);
   ASSERT_TRUE(TypeOf(acc)->Is<type::Pointer>());
@@ -892,13 +890,13 @@
   EXPECT_TRUE(ptr->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_ArrayAccessor_Vector) {
+TEST_F(ResolverTest, Expr_ArrayAccessor_Vector) {
   Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(acc), nullptr);
   ASSERT_TRUE(TypeOf(acc)->Is<type::Pointer>());
@@ -907,19 +905,19 @@
   EXPECT_TRUE(ptr->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Bitcast) {
+TEST_F(ResolverTest, Expr_Bitcast) {
   auto* bitcast = create<ast::BitcastExpression>(ty.f32(), Expr("name"));
   WrapInFunction(bitcast);
 
   Global("name", ty.f32(), ast::StorageClass::kPrivate);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(bitcast), nullptr);
   EXPECT_TRUE(TypeOf(bitcast)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Call) {
+TEST_F(ResolverTest, Expr_Call) {
   ast::VariableList params;
   Func("my_func", params, ty.f32(), ast::StatementList{},
        ast::FunctionDecorationList{});
@@ -927,13 +925,13 @@
   auto* call = Call("my_func");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Call_InBinaryOp) {
+TEST_F(ResolverTest, Expr_Call_InBinaryOp) {
   ast::VariableList params;
   Func("func", params, ty.f32(), ast::StatementList{},
        ast::FunctionDecorationList{});
@@ -941,13 +939,13 @@
   auto* expr = Add(Call("func"), Call("func"));
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Call_WithParams) {
+TEST_F(ResolverTest, Expr_Call_WithParams) {
   ast::VariableList params;
   Func("my_func", params, ty.f32(), ast::StatementList{},
        ast::FunctionDecorationList{});
@@ -957,70 +955,70 @@
   auto* call = Call("my_func", param);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(param), nullptr);
   EXPECT_TRUE(TypeOf(param)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Call_Intrinsic) {
+TEST_F(ResolverTest, Expr_Call_Intrinsic) {
   auto* call = Call("round", 2.4f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_DontCall_Function) {
+TEST_F(ResolverTest, Expr_DontCall_Function) {
   Func("func", {}, ty.void_(), {}, {});
   auto* ident = create<ast::IdentifierExpression>(
       Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
       Symbols().Register("func"));
   WrapInFunction(ident);
 
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(), "3:8 error: missing '(' for function call");
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(), "3:8 error: missing '(' for function call");
 }
 
-TEST_F(TypeDeterminerTest, Expr_DontCall_Intrinsic) {
+TEST_F(ResolverTest, Expr_DontCall_Intrinsic) {
   auto* ident = create<ast::IdentifierExpression>(
       Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
       Symbols().Register("round"));
   WrapInFunction(ident);
 
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(), "3:8 error: missing '(' for intrinsic call");
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(), "3:8 error: missing '(' for intrinsic call");
 }
 
-TEST_F(TypeDeterminerTest, Expr_Cast) {
+TEST_F(ResolverTest, Expr_Cast) {
   Global("name", ty.f32(), ast::StorageClass::kPrivate);
 
   auto* cast = Construct(ty.f32(), "name");
   WrapInFunction(cast);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(cast), nullptr);
   EXPECT_TRUE(TypeOf(cast)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Constructor_Scalar) {
+TEST_F(ResolverTest, Expr_Constructor_Scalar) {
   auto* s = Expr(1.0f);
   WrapInFunction(s);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(s), nullptr);
   EXPECT_TRUE(TypeOf(s)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Constructor_Type) {
+TEST_F(ResolverTest, Expr_Constructor_Type) {
   auto* tc = vec3<f32>(1.0f, 1.0f, 3.0f);
   WrapInFunction(tc);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(tc), nullptr);
   ASSERT_TRUE(TypeOf(tc)->Is<type::Vector>());
@@ -1028,13 +1026,13 @@
   EXPECT_EQ(TypeOf(tc)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Identifier_GlobalVariable) {
+TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
   auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kNone);
 
   auto* ident = Expr("my_var");
   WrapInFunction(ident);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(ident), nullptr);
   EXPECT_TRUE(TypeOf(ident)->Is<type::Pointer>());
@@ -1042,20 +1040,20 @@
   EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
 }
 
-TEST_F(TypeDeterminerTest, Expr_Identifier_GlobalConstant) {
+TEST_F(ResolverTest, Expr_Identifier_GlobalConstant) {
   auto* my_var = GlobalConst("my_var", ty.f32());
 
   auto* ident = Expr("my_var");
   WrapInFunction(ident);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(ident), nullptr);
   EXPECT_TRUE(TypeOf(ident)->Is<type::F32>());
   EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
 }
 
-TEST_F(TypeDeterminerTest, Expr_Identifier_FunctionVariable_Const) {
+TEST_F(ResolverTest, Expr_Identifier_FunctionVariable_Const) {
   auto* my_var_a = Expr("my_var");
   auto* my_var_b = Expr("my_var");
   auto* var = Const("my_var", ty.f32());
@@ -1068,7 +1066,7 @@
        },
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(my_var_a), nullptr);
   EXPECT_TRUE(TypeOf(my_var_a)->Is<type::F32>());
@@ -1079,7 +1077,7 @@
   EXPECT_TRUE(CheckVarUsers(var, {my_var_a, my_var_b}));
 }
 
-TEST_F(TypeDeterminerTest, Expr_Identifier_FunctionVariable) {
+TEST_F(ResolverTest, Expr_Identifier_FunctionVariable) {
   auto* my_var_a = Expr("my_var");
   auto* my_var_b = Expr("my_var");
   auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
@@ -1093,7 +1091,7 @@
        },
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(my_var_a), nullptr);
   EXPECT_TRUE(TypeOf(my_var_a)->Is<type::Pointer>());
@@ -1106,7 +1104,7 @@
   EXPECT_TRUE(CheckVarUsers(var, {my_var_a, my_var_b}));
 }
 
-TEST_F(TypeDeterminerTest, Expr_Identifier_Function_Ptr) {
+TEST_F(ResolverTest, Expr_Identifier_Function_Ptr) {
   auto* my_var_a = Expr("my_var");
   auto* my_var_b = Expr("my_var");
   auto* assign = create<ast::AssignmentStatement>(my_var_a, my_var_b);
@@ -1120,7 +1118,7 @@
        },
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(my_var_a), nullptr);
   EXPECT_TRUE(TypeOf(my_var_a)->Is<type::Pointer>());
@@ -1132,27 +1130,60 @@
   EXPECT_EQ(StmtOf(my_var_b), assign);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Call_Function) {
+TEST_F(ResolverTest, Expr_Call_Function) {
   Func("my_func", ast::VariableList{}, ty.f32(), ast::StatementList{},
        ast::FunctionDecorationList{});
 
   auto* call = Call("my_func");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Identifier_Unknown) {
+TEST_F(ResolverTest, Expr_Identifier_Unknown) {
   auto* a = Expr("a");
   WrapInFunction(a);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 }
 
-TEST_F(TypeDeterminerTest, Function_RegisterInputOutputVariables) {
+TEST_F(ResolverTest, UsingUndefinedVariable_Fail) {
+  // b = 2;
+
+  SetSource(Source{Source::Location{12, 34}});
+  auto* lhs = Expr("b");
+  auto* rhs = Expr(2);
+  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
+  WrapInFunction(assign);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: v-0006: identifier must be declared before use: b");
+}
+
+TEST_F(ResolverTest, UsingUndefinedVariableInBlockStatement_Fail) {
+  // {
+  //  b = 2;
+  // }
+
+  SetSource(Source{Source::Location{12, 34}});
+  auto* lhs = Expr("b");
+  auto* rhs = Expr(2);
+
+  auto* body = create<ast::BlockStatement>(ast::StatementList{
+      create<ast::AssignmentStatement>(lhs, rhs),
+  });
+  WrapInFunction(body);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: v-0006: identifier must be declared before use: b");
+}
+
+TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
   auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
   auto* out_var = Global("out_var", ty.f32(), ast::StorageClass::kOutput);
   auto* sb_var = Global("sb_var", ty.f32(), ast::StorageClass::kStorage);
@@ -1169,7 +1200,7 @@
       },
       ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   auto* func_sem = Sem().Get(func);
   ASSERT_NE(func_sem, nullptr);
@@ -1183,7 +1214,7 @@
   EXPECT_EQ(vars[4]->Declaration(), priv_var);
 }
 
-TEST_F(TypeDeterminerTest, Function_RegisterInputOutputVariables_SubFunction) {
+TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
   auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
   auto* out_var = Global("out_var", ty.f32(), ast::StorageClass::kOutput);
   auto* sb_var = Global("sb_var", ty.f32(), ast::StorageClass::kStorage);
@@ -1206,7 +1237,7 @@
       },
       ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   auto* func2_sem = Sem().Get(func2);
   ASSERT_NE(func2_sem, nullptr);
@@ -1220,7 +1251,7 @@
   EXPECT_EQ(vars[4]->Declaration(), priv_var);
 }
 
-TEST_F(TypeDeterminerTest, Function_NotRegisterFunctionVariable) {
+TEST_F(ResolverTest, Function_NotRegisterFunctionVariable) {
   auto* var = Var("in_var", ty.f32(), ast::StorageClass::kFunction);
 
   auto* func =
@@ -1233,7 +1264,7 @@
 
   Global("var", ty.f32(), ast::StorageClass::kFunction);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   auto* func_sem = Sem().Get(func);
   ASSERT_NE(func_sem, nullptr);
@@ -1241,7 +1272,7 @@
   EXPECT_EQ(func_sem->ReferencedModuleVariables().size(), 0u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_Struct) {
+TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
   auto* strct = create<ast::Struct>(
       ast::StructMemberList{Member("first_member", ty.i32()),
                             Member("second_member", ty.f32())},
@@ -1253,7 +1284,7 @@
   auto* mem = MemberAccessor("my_struct", "second_member");
   WrapInFunction(mem);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(mem), nullptr);
   ASSERT_TRUE(TypeOf(mem)->Is<type::Pointer>());
@@ -1262,7 +1293,7 @@
   EXPECT_TRUE(ptr->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_Struct_Alias) {
+TEST_F(ResolverTest, Expr_MemberAccessor_Struct_Alias) {
   auto* strct = create<ast::Struct>(
       ast::StructMemberList{Member("first_member", ty.i32()),
                             Member("second_member", ty.f32())},
@@ -1275,7 +1306,7 @@
   auto* mem = MemberAccessor("my_struct", "second_member");
   WrapInFunction(mem);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(mem), nullptr);
   ASSERT_TRUE(TypeOf(mem)->Is<type::Pointer>());
@@ -1284,13 +1315,13 @@
   EXPECT_TRUE(ptr->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_VectorSwizzle) {
+TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle) {
   Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* mem = MemberAccessor("my_vec", "xzyw");
   WrapInFunction(mem);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(mem), nullptr);
   ASSERT_TRUE(TypeOf(mem)->Is<type::Vector>());
@@ -1299,13 +1330,13 @@
   EXPECT_THAT(Sem().Get(mem)->Swizzle(), ElementsAre(0, 2, 1, 3));
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
+TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
   Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* mem = MemberAccessor("my_vec", "b");
   WrapInFunction(mem);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(mem), nullptr);
   ASSERT_TRUE(TypeOf(mem)->Is<type::Pointer>());
@@ -1315,7 +1346,7 @@
   EXPECT_THAT(Sem().Get(mem)->Swizzle(), ElementsAre(2));
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
+TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
   Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* ident = create<ast::IdentifierExpression>(
@@ -1325,11 +1356,11 @@
   auto* mem = MemberAccessor("my_vec", ident);
   WrapInFunction(mem);
 
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(), "3:5 error: invalid vector swizzle character");
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(), "3:5 error: invalid vector swizzle character");
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
+TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
   Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* ident = create<ast::IdentifierExpression>(
@@ -1339,13 +1370,13 @@
   auto* mem = MemberAccessor("my_vec", ident);
   WrapInFunction(mem);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(
-      td()->error(),
+      r()->error(),
       "3:3 error: invalid mixing of vector swizzle characters rgba with xyzw");
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
+TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
   Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* ident = create<ast::IdentifierExpression>(
@@ -1354,11 +1385,11 @@
   auto* mem = MemberAccessor("my_vec", ident);
   WrapInFunction(mem);
 
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(), "3:3 error: invalid vector swizzle size");
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle size");
 }
 
-TEST_F(TypeDeterminerTest, Expr_Accessor_MultiLevel) {
+TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {
   // struct b {
   //   vec4<f32> foo
   // }
@@ -1401,7 +1432,7 @@
       "yx");
   WrapInFunction(mem);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(mem), nullptr);
   ASSERT_TRUE(TypeOf(mem)->Is<type::Vector>());
@@ -1409,7 +1440,7 @@
   EXPECT_EQ(TypeOf(mem)->As<type::Vector>()->size(), 2u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_MemberAccessor_InBinaryOp) {
+TEST_F(ResolverTest, Expr_MemberAccessor_InBinaryOp) {
   auto* strct = create<ast::Struct>(
       ast::StructMemberList{Member("first_member", ty.f32()),
                             Member("second_member", ty.f32())},
@@ -1422,13 +1453,13 @@
                    MemberAccessor("my_struct", "second_member"));
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
 }
 
-using Expr_Binary_BitwiseTest = TypeDeterminerTestWithParam<ast::BinaryOp>;
+using Expr_Binary_BitwiseTest = ResolverTestWithParam<ast::BinaryOp>;
 TEST_P(Expr_Binary_BitwiseTest, Scalar) {
   auto op = GetParam();
 
@@ -1437,7 +1468,7 @@
   auto* expr = create<ast::BinaryExpression>(op, Expr("val"), Expr("val"));
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::I32>());
 }
@@ -1450,13 +1481,13 @@
   auto* expr = create<ast::BinaryExpression>(op, Expr("val"), Expr("val"));
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::I32>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          Expr_Binary_BitwiseTest,
                          testing::Values(ast::BinaryOp::kAnd,
                                          ast::BinaryOp::kOr,
@@ -1468,7 +1499,7 @@
                                          ast::BinaryOp::kDivide,
                                          ast::BinaryOp::kModulo));
 
-using Expr_Binary_LogicalTest = TypeDeterminerTestWithParam<ast::BinaryOp>;
+using Expr_Binary_LogicalTest = ResolverTestWithParam<ast::BinaryOp>;
 TEST_P(Expr_Binary_LogicalTest, Scalar) {
   auto op = GetParam();
 
@@ -1477,7 +1508,7 @@
   auto* expr = create<ast::BinaryExpression>(op, Expr("val"), Expr("val"));
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
 }
@@ -1490,18 +1521,18 @@
   auto* expr = create<ast::BinaryExpression>(op, Expr("val"), Expr("val"));
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::Bool>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          Expr_Binary_LogicalTest,
                          testing::Values(ast::BinaryOp::kLogicalAnd,
                                          ast::BinaryOp::kLogicalOr));
 
-using Expr_Binary_CompareTest = TypeDeterminerTestWithParam<ast::BinaryOp>;
+using Expr_Binary_CompareTest = ResolverTestWithParam<ast::BinaryOp>;
 TEST_P(Expr_Binary_CompareTest, Scalar) {
   auto op = GetParam();
 
@@ -1510,7 +1541,7 @@
   auto* expr = create<ast::BinaryExpression>(op, Expr("val"), Expr("val"));
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
 }
@@ -1523,13 +1554,13 @@
   auto* expr = create<ast::BinaryExpression>(op, Expr("val"), Expr("val"));
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::Bool>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          Expr_Binary_CompareTest,
                          testing::Values(ast::BinaryOp::kEqual,
                                          ast::BinaryOp::kNotEqual,
@@ -1538,66 +1569,66 @@
                                          ast::BinaryOp::kLessThanEqual,
                                          ast::BinaryOp::kGreaterThanEqual));
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Scalar_Scalar) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Scalar_Scalar) {
   Global("val", ty.i32(), ast::StorageClass::kNone);
 
   auto* expr = Mul("val", "val");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::I32>());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Vector_Scalar) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Vector_Scalar) {
   Global("scalar", ty.f32(), ast::StorageClass::kNone);
   Global("vector", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("vector", "scalar");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Scalar_Vector) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Scalar_Vector) {
   Global("scalar", ty.f32(), ast::StorageClass::kNone);
   Global("vector", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("scalar", "vector");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Vector_Vector) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Vector_Vector) {
   Global("vector", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("vector", "vector");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Matrix_Scalar) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Matrix_Scalar) {
   Global("scalar", ty.f32(), ast::StorageClass::kNone);
   Global("matrix", ty.mat2x3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("matrix", "scalar");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Matrix>());
 
@@ -1607,14 +1638,14 @@
   EXPECT_EQ(mat->columns(), 2u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Scalar_Matrix) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Scalar_Matrix) {
   Global("scalar", ty.f32(), ast::StorageClass::kNone);
   Global("matrix", ty.mat2x3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("scalar", "matrix");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Matrix>());
 
@@ -1624,42 +1655,42 @@
   EXPECT_EQ(mat->columns(), 2u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Matrix_Vector) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Matrix_Vector) {
   Global("vector", ty.vec3<f32>(), ast::StorageClass::kNone);
   Global("matrix", ty.mat2x3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("matrix", "vector");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Vector_Matrix) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Vector_Matrix) {
   Global("vector", ty.vec3<f32>(), ast::StorageClass::kNone);
   Global("matrix", ty.mat2x3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("vector", "matrix");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
   EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 2u);
 }
 
-TEST_F(TypeDeterminerTest, Expr_Binary_Multiply_Matrix_Matrix) {
+TEST_F(ResolverTest, Expr_Binary_Multiply_Matrix_Matrix) {
   Global("mat3x4", ty.mat3x4<f32>(), ast::StorageClass::kNone);
   Global("mat4x3", ty.mat4x3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Mul("mat3x4", "mat4x3");
   WrapInFunction(expr);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Matrix>());
 
@@ -1669,7 +1700,7 @@
   EXPECT_EQ(mat->columns(), 4u);
 }
 
-using IntrinsicDerivativeTest = TypeDeterminerTestWithParam<std::string>;
+using IntrinsicDerivativeTest = ResolverTestWithParam<std::string>;
 TEST_P(IntrinsicDerivativeTest, Scalar) {
   auto name = GetParam();
 
@@ -1678,7 +1709,7 @@
   auto* expr = Call(name, "ident");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::F32>());
@@ -1691,7 +1722,7 @@
   auto* expr = Call(name, "ident");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
@@ -1705,16 +1736,16 @@
   auto* expr = Call(name);
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(), "error: no matching call to " + name +
-                               "()\n\n"
-                               "2 candidate functions:\n  " +
-                               name + "(f32) -> f32\n  " + name +
-                               "(vecN<f32>) -> vecN<f32>\n");
+  EXPECT_EQ(r()->error(), "error: no matching call to " + name +
+                              "()\n\n"
+                              "2 candidate functions:\n  " +
+                              name + "(f32) -> f32\n  " + name +
+                              "(vecN<f32>) -> vecN<f32>\n");
 }
 
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          IntrinsicDerivativeTest,
                          testing::Values("dpdx",
                                          "dpdxCoarse",
@@ -1726,7 +1757,7 @@
                                          "fwidthCoarse",
                                          "fwidthFine"));
 
-using Intrinsic = TypeDeterminerTestWithParam<std::string>;
+using Intrinsic = ResolverTestWithParam<std::string>;
 TEST_P(Intrinsic, Test) {
   auto name = GetParam();
 
@@ -1735,16 +1766,16 @@
   auto* expr = Call(name, "my_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
 }
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          Intrinsic,
                          testing::Values("any", "all"));
 
-using Intrinsic_FloatMethod = TypeDeterminerTestWithParam<std::string>;
+using Intrinsic_FloatMethod = ResolverTestWithParam<std::string>;
 TEST_P(Intrinsic_FloatMethod, Vector) {
   auto name = GetParam();
 
@@ -1753,7 +1784,7 @@
   auto* expr = Call(name, "my_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
@@ -1769,7 +1800,7 @@
   auto* expr = Call(name, "my_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
@@ -1783,13 +1814,13 @@
   auto* expr = Call(name);
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(), "error: no matching call to " + name +
-                               "()\n\n"
-                               "2 candidate functions:\n  " +
-                               name + "(f32) -> bool\n  " + name +
-                               "(vecN<f32>) -> vecN<bool>\n");
+  EXPECT_EQ(r()->error(), "error: no matching call to " + name +
+                              "()\n\n"
+                              "2 candidate functions:\n  " +
+                              name + "(f32) -> bool\n  " + name +
+                              "(vecN<f32>) -> vecN<bool>\n");
 }
 
 TEST_P(Intrinsic_FloatMethod, TooManyParams) {
@@ -1800,16 +1831,16 @@
   auto* expr = Call(name, "my_var", 1.23f);
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(), "error: no matching call to " + name +
-                               "(ptr<f32>, f32)\n\n"
-                               "2 candidate functions:\n  " +
-                               name + "(f32) -> bool\n  " + name +
-                               "(vecN<f32>) -> vecN<bool>\n");
+  EXPECT_EQ(r()->error(), "error: no matching call to " + name +
+                              "(ptr<f32>, f32)\n\n"
+                              "2 candidate functions:\n  " +
+                              name + "(f32) -> bool\n  " + name +
+                              "(vecN<f32>) -> vecN<bool>\n");
 }
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_FloatMethod,
     testing::Values("isInf", "isNan", "isFinite", "isNormal"));
 
@@ -1836,7 +1867,7 @@
 }
 
 class Intrinsic_TextureOperation
-    : public TypeDeterminerTestWithParam<TextureTestParams> {
+    : public ResolverTestWithParam<TextureTestParams> {
  public:
   /// Gets an appropriate type for the coords parameter depending the the
   /// dimensionality of the texture being sampled.
@@ -1902,7 +1933,7 @@
   auto* expr = Call("textureLoad", call_params);
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
@@ -1917,7 +1948,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_StorageTextureOperation,
     testing::Values(
         TextureTestParams{type::TextureDimension::k1d, Texture::kF32,
@@ -1966,7 +1997,7 @@
   auto* expr = Call("textureLoad", call_params);
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
@@ -1981,56 +2012,56 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_SampledTextureOperation,
     testing::Values(TextureTestParams{type::TextureDimension::k1d},
                     TextureTestParams{type::TextureDimension::k2d},
                     TextureTestParams{type::TextureDimension::k2dArray},
                     TextureTestParams{type::TextureDimension::k3d}));
 
-TEST_F(TypeDeterminerTest, Intrinsic_Dot_Vec2) {
+TEST_F(ResolverTest, Intrinsic_Dot_Vec2) {
   Global("my_var", ty.vec2<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Dot_Vec3) {
+TEST_F(ResolverTest, Intrinsic_Dot_Vec3) {
   Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Dot_Vec4) {
+TEST_F(ResolverTest, Intrinsic_Dot_Vec4) {
   Global("my_var", ty.vec4<f32>(), ast::StorageClass::kNone);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Dot_Error_Scalar) {
+TEST_F(ResolverTest, Intrinsic_Dot_Error_Scalar) {
   auto* expr = Call("dot", 1.0f, 1.0f);
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to dot(f32, f32)
 
 1 candidate function:
@@ -2038,15 +2069,15 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Dot_Error_VectorInt) {
+TEST_F(ResolverTest, Intrinsic_Dot_Error_VectorInt) {
   Global("my_var", ty.vec4<i32>(), ast::StorageClass::kNone);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to dot(ptr<vec4<i32>>, ptr<vec4<i32>>)
 
 1 candidate function:
@@ -2054,7 +2085,7 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Select) {
+TEST_F(ResolverTest, Intrinsic_Select) {
   Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
 
   Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kNone);
@@ -2062,7 +2093,7 @@
   auto* expr = Call("select", "my_var", "my_var", "bool_var");
   WrapInFunction(expr);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(expr), nullptr);
   EXPECT_TRUE(TypeOf(expr)->Is<type::Vector>());
@@ -2070,13 +2101,13 @@
   EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_NoParams) {
+TEST_F(ResolverTest, Intrinsic_Select_Error_NoParams) {
   auto* expr = Call("select");
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to select()
 
 2 candidate functions:
@@ -2085,13 +2116,13 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_SelectorInt) {
+TEST_F(ResolverTest, Intrinsic_Select_Error_SelectorInt) {
   auto* expr = Call("select", Expr(1), Expr(1), Expr(1));
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to select(i32, i32, i32)
 
 2 candidate functions:
@@ -2100,14 +2131,14 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_Matrix) {
+TEST_F(ResolverTest, Intrinsic_Select_Error_Matrix) {
   auto* mat = mat2x2<float>(vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f));
   auto* expr = Call("select", mat, mat, Expr(true));
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
 
 2 candidate functions:
@@ -2116,13 +2147,13 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_MismatchTypes) {
+TEST_F(ResolverTest, Intrinsic_Select_Error_MismatchTypes) {
   auto* expr = Call("select", 1.0f, vec2<float>(2.0f, 3.0f), Expr(true));
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to select(f32, vec2<f32>, bool)
 
 2 candidate functions:
@@ -2131,14 +2162,14 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Select_Error_MismatchVectorSize) {
+TEST_F(ResolverTest, Intrinsic_Select_Error_MismatchVectorSize) {
   auto* expr = Call("select", vec2<float>(1.0f, 2.0f),
                     vec3<float>(3.0f, 4.0f, 5.0f), Expr(true));
   WrapInFunction(expr);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
 
 2 candidate functions:
@@ -2147,7 +2178,7 @@
 )");
 }
 
-using UnaryOpExpressionTest = TypeDeterminerTestWithParam<ast::UnaryOp>;
+using UnaryOpExpressionTest = ResolverTestWithParam<ast::UnaryOp>;
 TEST_P(UnaryOpExpressionTest, Expr_UnaryOp) {
   auto op = GetParam();
 
@@ -2155,51 +2186,51 @@
   auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
   WrapInFunction(der);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(der), nullptr);
   ASSERT_TRUE(TypeOf(der)->Is<type::Vector>());
   EXPECT_TRUE(TypeOf(der)->As<type::Vector>()->type()->Is<type::F32>());
   EXPECT_EQ(TypeOf(der)->As<type::Vector>()->size(), 4u);
 }
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          UnaryOpExpressionTest,
                          testing::Values(ast::UnaryOp::kNegation,
                                          ast::UnaryOp::kNot));
 
-TEST_F(TypeDeterminerTest, StorageClass_SetsIfMissing) {
+TEST_F(ResolverTest, StorageClass_SetsIfMissing) {
   auto* var = Var("var", ty.i32(), ast::StorageClass::kNone);
 
   auto* stmt = create<ast::VariableDeclStatement>(var);
   Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kFunction);
 }
 
-TEST_F(TypeDeterminerTest, StorageClass_DoesNotSetOnConst) {
+TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
   auto* var = Const("var", ty.i32());
   auto* stmt = create<ast::VariableDeclStatement>(var);
   Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
        ast::FunctionDecorationList{});
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kNone);
 }
 
-TEST_F(TypeDeterminerTest, StorageClass_NonFunctionClassError) {
+TEST_F(ResolverTest, StorageClass_NonFunctionClassError) {
   auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
 
   auto* stmt = create<ast::VariableDeclStatement>(var);
   Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
        ast::FunctionDecorationList{});
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: function variable has a non-function storage class");
 }
 
@@ -2213,7 +2244,7 @@
   return out;
 }
 
-using Intrinsic_DataPackingTest = TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_DataPackingTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_DataPackingTest, InferType) {
   auto param = GetParam();
 
@@ -2224,7 +2255,7 @@
                      : Call(param.name, vec2<f32>(1.f, 2.f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
 }
@@ -2239,10 +2270,10 @@
                      : Call(param.name, vec2<i32>(1, 2));
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
-                                       std::string(param.name)));
+  EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
+                                      std::string(param.name)));
 }
 
 TEST_P(Intrinsic_DataPackingTest, Error_NoParams) {
@@ -2251,10 +2282,10 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
-                                       std::string(param.name)));
+  EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
+                                      std::string(param.name)));
 }
 
 TEST_P(Intrinsic_DataPackingTest, Error_TooManyParams) {
@@ -2267,14 +2298,14 @@
                      : Call(param.name, vec2<f32>(1.f, 2.f), 1.0f);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_THAT(td()->error(), HasSubstr("error: no matching call to " +
-                                       std::string(param.name)));
+  EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
+                                      std::string(param.name)));
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_DataPackingTest,
     testing::Values(
         IntrinsicData{"pack4x8snorm", IntrinsicType::kPack4x8Snorm},
@@ -2283,7 +2314,7 @@
         IntrinsicData{"pack2x16unorm", IntrinsicType::kPack2x16Unorm},
         IntrinsicData{"pack2x16float", IntrinsicType::kPack2x16Float}));
 
-using Intrinsic_DataUnpackingTest = TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_DataUnpackingTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_DataUnpackingTest, InferType) {
   auto param = GetParam();
 
@@ -2293,7 +2324,7 @@
   auto* call = Call(param.name, 1u);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
   if (pack4) {
@@ -2304,7 +2335,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_DataUnpackingTest,
     testing::Values(
         IntrinsicData{"unpack4x8snorm", IntrinsicType::kUnpack4x8Snorm},
@@ -2313,14 +2344,14 @@
         IntrinsicData{"unpack2x16unorm", IntrinsicType::kUnpack2x16Unorm},
         IntrinsicData{"unpack2x16float", IntrinsicType::kUnpack2x16Float}));
 
-using Intrinsic_SingleParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_SingleParamTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_SingleParamTest, Scalar) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
@@ -2332,7 +2363,7 @@
   auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -2345,9 +2376,9 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "()\n\n"
                 "2 candidate functions:\n  " +
@@ -2361,9 +2392,9 @@
   auto* call = Call(param.name, 1, 2, 3);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "(i32, i32, i32)\n\n"
                 "2 candidate functions:\n  " +
@@ -2372,7 +2403,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_SingleParamTest,
     testing::Values(IntrinsicData{"acos", IntrinsicType::kAcos},
                     IntrinsicData{"asin", IntrinsicType::kAsin},
@@ -2396,14 +2427,14 @@
                     IntrinsicData{"tanh", IntrinsicType::kTanh},
                     IntrinsicData{"trunc", IntrinsicType::kTrunc}));
 
-using IntrinsicDataTest = TypeDeterminerTest;
+using IntrinsicDataTest = ResolverTest;
 
 TEST_F(IntrinsicDataTest, ArrayLength_Vector) {
   Global("arr", ty.array<int>(), ast::StorageClass::kNone);
   auto* call = Call("arrayLength", "arr");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
@@ -2414,9 +2445,9 @@
   auto* call = Call("arrayLength", "arr");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to arrayLength(ptr<array<i32, 4>>)\n\n"
             "1 candidate function:\n"
             "  arrayLength(array<T>) -> u32\n");
@@ -2426,7 +2457,7 @@
   auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -2437,9 +2468,9 @@
   auto* call = Call("normalize");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to normalize()\n\n"
             "1 candidate function:\n"
             "  normalize(vecN<f32>) -> vecN<f32>\n");
@@ -2450,7 +2481,7 @@
   auto* call = Call("frexp", 1.0f, "exp");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
@@ -2461,7 +2492,7 @@
   auto* call = Call("frexp", vec3<f32>(1.0f, 2.0f, 3.0f), "exp");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::Vector>());
@@ -2473,9 +2504,9 @@
   auto* call = Call("frexp", 1, "exp");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to frexp(i32, ptr<workgroup, i32>)\n\n"
             "2 candidate functions:\n"
             "  frexp(f32, ptr<T>) -> f32  where: T is i32 or u32\n"
@@ -2488,9 +2519,9 @@
   auto* call = Call("frexp", 1.0f, "exp");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to frexp(f32, ptr<workgroup, f32>)\n\n"
             "2 candidate functions:\n"
             "  frexp(f32, ptr<T>) -> f32  where: T is i32 or u32\n"
@@ -2502,9 +2533,9 @@
   auto* call = Call("frexp", 1.0f, 1);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to frexp(f32, i32)\n\n"
             "2 candidate functions:\n"
             "  frexp(f32, ptr<T>) -> f32  where: T is i32 or u32\n"
@@ -2517,9 +2548,9 @@
   auto* call = Call("frexp", vec2<f32>(1.0f, 2.0f), "exp");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to frexp(vec2<f32>, ptr<workgroup, "
             "vec4<i32>>)\n\n"
             "2 candidate functions:\n"
@@ -2533,7 +2564,7 @@
   auto* call = Call("modf", 1.0f, "whole");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
@@ -2544,7 +2575,7 @@
   auto* call = Call("modf", vec3<f32>(1.0f, 2.0f, 3.0f), "whole");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::Vector>());
@@ -2556,9 +2587,9 @@
   auto* call = Call("modf", 1, "whole");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to modf(i32, ptr<workgroup, f32>)\n\n"
             "2 candidate functions:\n"
             "  modf(f32, ptr<f32>) -> f32\n"
@@ -2570,9 +2601,9 @@
   auto* call = Call("modf", 1.0f, "whole");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to modf(f32, ptr<workgroup, i32>)\n\n"
             "2 candidate functions:\n"
             "  modf(f32, ptr<f32>) -> f32\n"
@@ -2583,9 +2614,9 @@
   auto* call = Call("modf", 1.0f, 1.0f);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to modf(f32, f32)\n\n"
             "2 candidate functions:\n"
             "  modf(f32, ptr<f32>) -> f32\n"
@@ -2597,9 +2628,9 @@
   auto* call = Call("modf", vec2<f32>(1.0f, 2.0f), "whole");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to modf(vec2<f32>, ptr<workgroup, "
             "vec4<f32>>)\n\n"
             "2 candidate functions:\n"
@@ -2608,14 +2639,14 @@
 }
 
 using Intrinsic_SingleParam_FloatOrInt_Test =
-    TypeDeterminerTestWithParam<IntrinsicData>;
+    ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Float_Scalar) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
@@ -2627,7 +2658,7 @@
   auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -2640,7 +2671,7 @@
   auto* call = Call(param.name, -1);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
@@ -2660,7 +2691,7 @@
   auto* call = Call(param.name, params);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
@@ -2676,7 +2707,7 @@
   auto* call = Call(param.name, params);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
@@ -2688,7 +2719,7 @@
   auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
@@ -2701,9 +2732,9 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "()\n\n"
                 "2 candidate functions:\n  " +
@@ -2713,42 +2744,42 @@
                 "(vecN<T>) -> vecN<T>  where: T is f32, i32 or u32\n");
 }
 
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          Intrinsic_SingleParam_FloatOrInt_Test,
                          testing::Values(IntrinsicData{"abs",
                                                        IntrinsicType::kAbs}));
 
-TEST_F(TypeDeterminerTest, Intrinsic_Length_Scalar) {
+TEST_F(ResolverTest, Intrinsic_Length_Scalar) {
   auto* call = Call("length", 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Length_FloatVector) {
+TEST_F(ResolverTest, Intrinsic_Length_FloatVector) {
   ast::ExpressionList params;
   params.push_back(vec3<f32>(1.0f, 1.0f, 3.0f));
 
   auto* call = Call("length", params);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
 }
 
-using Intrinsic_TwoParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_TwoParamTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_TwoParamTest, Scalar) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1.f, 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
@@ -2761,7 +2792,7 @@
                     vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -2774,9 +2805,9 @@
   auto* call = Call(param.name, 1, 2, 3);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "(i32, i32, i32)\n\n"
                 "2 candidate functions:\n  " +
@@ -2791,9 +2822,9 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "()\n\n"
                 "2 candidate functions:\n  " +
@@ -2803,79 +2834,79 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_TwoParamTest,
     testing::Values(IntrinsicData{"atan2", IntrinsicType::kAtan2},
                     IntrinsicData{"pow", IntrinsicType::kPow},
                     IntrinsicData{"step", IntrinsicType::kStep},
                     IntrinsicData{"reflect", IntrinsicType::kReflect}));
 
-TEST_F(TypeDeterminerTest, Intrinsic_Distance_Scalar) {
+TEST_F(ResolverTest, Intrinsic_Distance_Scalar) {
   auto* call = Call("distance", 1.f, 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Distance_Vector) {
+TEST_F(ResolverTest, Intrinsic_Distance_Vector) {
   auto* call = Call("distance", vec3<f32>(1.0f, 1.0f, 3.0f),
                     vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Cross) {
+TEST_F(ResolverTest, Intrinsic_Cross) {
   auto* call =
       Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
   EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_NoArgs) {
+TEST_F(ResolverTest, Intrinsic_Cross_Error_NoArgs) {
   auto* call = Call("cross");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(), R"(error: no matching call to cross()
+  EXPECT_EQ(r()->error(), R"(error: no matching call to cross()
 
 1 candidate function:
   cross(vec3<f32>, vec3<f32>) -> vec3<f32>
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Scalar) {
+TEST_F(ResolverTest, Intrinsic_Cross_Error_Scalar) {
   auto* call = Call("cross", 1.0f, 1.0f);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(), R"(error: no matching call to cross(f32, f32)
+  EXPECT_EQ(r()->error(), R"(error: no matching call to cross(f32, f32)
 
 1 candidate function:
   cross(vec3<f32>, vec3<f32>) -> vec3<f32>
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Vec3Int) {
+TEST_F(ResolverTest, Intrinsic_Cross_Error_Vec3Int) {
   auto* call = Call("cross", vec3<i32>(1, 2, 3), vec3<i32>(1, 2, 3));
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to cross(vec3<i32>, vec3<i32>)
 
 1 candidate function:
@@ -2883,15 +2914,15 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_Vec4) {
+TEST_F(ResolverTest, Intrinsic_Cross_Error_Vec4) {
   auto* call = Call("cross", vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f),
                     vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
 
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to cross(vec4<f32>, vec4<f32>)
 
 1 candidate function:
@@ -2899,53 +2930,53 @@
 )");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Cross_Error_TooManyParams) {
+TEST_F(ResolverTest, Intrinsic_Cross_Error_TooManyParams) {
   auto* call = Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f),
                     vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
 
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             R"(error: no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
 
 1 candidate function:
   cross(vec3<f32>, vec3<f32>) -> vec3<f32>
 )");
 }
-TEST_F(TypeDeterminerTest, Intrinsic_Normalize) {
+TEST_F(ResolverTest, Intrinsic_Normalize) {
   auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
   EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Normalize_NoArgs) {
+TEST_F(ResolverTest, Intrinsic_Normalize_NoArgs) {
   auto* call = Call("normalize");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(), R"(error: no matching call to normalize()
+  EXPECT_EQ(r()->error(), R"(error: no matching call to normalize()
 
 1 candidate function:
   normalize(vecN<f32>) -> vecN<f32>
 )");
 }
 
-using Intrinsic_ThreeParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_ThreeParamTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_ThreeParamTest, Scalar) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1.f, 1.f, 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
@@ -2958,7 +2989,7 @@
                     vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -2970,9 +3001,9 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "()\n\n"
                 "2 candidate functions:\n  " +
@@ -2982,7 +3013,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_ThreeParamTest,
     testing::Values(IntrinsicData{"mix", IntrinsicType::kMix},
                     IntrinsicData{"smoothStep", IntrinsicType::kSmoothStep},
@@ -2990,14 +3021,14 @@
                     IntrinsicData{"faceForward", IntrinsicType::kFaceForward}));
 
 using Intrinsic_ThreeParam_FloatOrInt_Test =
-    TypeDeterminerTestWithParam<IntrinsicData>;
+    ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Float_Scalar) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1.f, 1.f, 1.f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_scalar());
@@ -3010,7 +3041,7 @@
                     vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -3023,7 +3054,7 @@
   auto* call = Call(param.name, 1, 1, 1);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
@@ -3036,7 +3067,7 @@
                     vec3<i32>(1, 1, 3));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
@@ -3049,7 +3080,7 @@
   auto* call = Call(param.name, 1u, 1u, 1u);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
@@ -3062,7 +3093,7 @@
                     vec3<u32>(1u, 1u, 3u));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
@@ -3075,9 +3106,9 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "()\n\n"
                 "2 candidate functions:\n  " +
@@ -3088,20 +3119,19 @@
                 "or u32\n");
 }
 
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          Intrinsic_ThreeParam_FloatOrInt_Test,
                          testing::Values(IntrinsicData{"clamp",
                                                        IntrinsicType::kClamp}));
 
-using Intrinsic_Int_SingleParamTest =
-    TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_Int_SingleParamTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Int_SingleParamTest, Scalar) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_integer_scalar());
@@ -3113,7 +3143,7 @@
   auto* call = Call(param.name, vec3<i32>(1, 1, 3));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
@@ -3126,33 +3156,32 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
-            "error: no matching call to " + std::string(param.name) +
-                "()\n\n"
-                "2 candidate functions:\n  " +
-                std::string(param.name) +
-                "(T) -> T  where: T is i32 or u32\n  " +
-                std::string(param.name) +
-                "(vecN<T>) -> vecN<T>  where: T is i32 or u32\n");
+  EXPECT_EQ(r()->error(), "error: no matching call to " +
+                              std::string(param.name) +
+                              "()\n\n"
+                              "2 candidate functions:\n  " +
+                              std::string(param.name) +
+                              "(T) -> T  where: T is i32 or u32\n  " +
+                              std::string(param.name) +
+                              "(vecN<T>) -> vecN<T>  where: T is i32 or u32\n");
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_Int_SingleParamTest,
     testing::Values(IntrinsicData{"countOneBits", IntrinsicType::kCountOneBits},
                     IntrinsicData{"reverseBits", IntrinsicType::kReverseBits}));
 
-using Intrinsic_FloatOrInt_TwoParamTest =
-    TypeDeterminerTestWithParam<IntrinsicData>;
+using Intrinsic_FloatOrInt_TwoParamTest = ResolverTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Scalar_Signed) {
   auto param = GetParam();
 
   auto* call = Call(param.name, 1, 1);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
@@ -3164,7 +3193,7 @@
   auto* call = Call(param.name, 1u, 1u);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
@@ -3176,7 +3205,7 @@
   auto* call = Call(param.name, 1.0f, 1.0f);
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
@@ -3188,7 +3217,7 @@
   auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
@@ -3201,7 +3230,7 @@
   auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
@@ -3215,7 +3244,7 @@
       Call(param.name, vec3<f32>(1.f, 1.f, 3.f), vec3<f32>(1.f, 1.f, 3.f));
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->is_float_vector());
@@ -3228,9 +3257,9 @@
   auto* call = Call(param.name);
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to " + std::string(param.name) +
                 "()\n\n"
                 "2 candidate functions:\n  " +
@@ -3241,77 +3270,77 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
+    ResolverTest,
     Intrinsic_FloatOrInt_TwoParamTest,
     testing::Values(IntrinsicData{"min", IntrinsicType::kMin},
                     IntrinsicData{"max", IntrinsicType::kMax}));
 
-TEST_F(TypeDeterminerTest, Intrinsic_Determinant_2x2) {
+TEST_F(ResolverTest, Intrinsic_Determinant_2x2) {
   Global("var", ty.mat2x2<f32>(), ast::StorageClass::kFunction);
 
   auto* call = Call("determinant", "var");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Determinant_3x3) {
+TEST_F(ResolverTest, Intrinsic_Determinant_3x3) {
   Global("var", ty.mat3x3<f32>(), ast::StorageClass::kFunction);
 
   auto* call = Call("determinant", "var");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Determinant_4x4) {
+TEST_F(ResolverTest, Intrinsic_Determinant_4x4) {
   Global("var", ty.mat4x4<f32>(), ast::StorageClass::kFunction);
 
   auto* call = Call("determinant", "var");
   WrapInFunction(call);
 
-  EXPECT_TRUE(td()->Determine()) << td()->error();
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
 
   ASSERT_NE(TypeOf(call), nullptr);
   EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotSquare) {
+TEST_F(ResolverTest, Intrinsic_Determinant_NotSquare) {
   Global("var", ty.mat2x3<f32>(), ast::StorageClass::kFunction);
 
   auto* call = Call("determinant", "var");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
   EXPECT_EQ(
-      td()->error(),
+      r()->error(),
       "error: no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
       "1 candidate function:\n"
       "  determinant(matNxN<f32>) -> f32\n");
 }
 
-TEST_F(TypeDeterminerTest, Intrinsic_Determinant_NotMatrix) {
+TEST_F(ResolverTest, Intrinsic_Determinant_NotMatrix) {
   Global("var", ty.f32(), ast::StorageClass::kFunction);
 
   auto* call = Call("determinant", "var");
   WrapInFunction(call);
 
-  EXPECT_FALSE(td()->Determine());
+  EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(td()->error(),
+  EXPECT_EQ(r()->error(),
             "error: no matching call to determinant(ptr<function, f32>)\n\n"
             "1 candidate function:\n"
             "  determinant(matNxN<f32>) -> f32\n");
 }
 
-TEST_F(TypeDeterminerTest, Function_EntryPoints_StageDecoration) {
+TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
   // fn b() {}
   // fn c() { b(); }
   // fn a() { c(); }
@@ -3366,7 +3395,7 @@
   Global("call_b", ty.f32(), ast::StorageClass::kPrivate);
   Global("call_c", ty.f32(), ast::StorageClass::kPrivate);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
 
   auto* func_b_sem = Sem().Get(func_b);
   auto* func_a_sem = Sem().Get(func_a);
@@ -3399,7 +3428,7 @@
 
 // Check for linear-time traversal of functions reachable from entry points.
 // See: crbug.com/tint/245
-TEST_F(TypeDeterminerTest, Function_EntryPoints_LinearTime) {
+TEST_F(ResolverTest, Function_EntryPoints_LinearTime) {
   // fn lNa() { }
   // fn lNb() { }
   // ...
@@ -3441,15 +3470,15 @@
            create<ast::StageDecoration>(ast::PipelineStage::kVertex),
        });
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-using TypeDeterminerTextureIntrinsicTest =
-    TypeDeterminerTestWithParam<ast::intrinsic::test::TextureOverloadCase>;
+using ResolverTextureIntrinsicTest =
+    ResolverTestWithParam<ast::intrinsic::test::TextureOverloadCase>;
 
 INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
-    TypeDeterminerTextureIntrinsicTest,
+    ResolverTest,
+    ResolverTextureIntrinsicTest,
     testing::ValuesIn(ast::intrinsic::test::TextureOverloadCase::ValidCases()));
 
 std::string to_str(const std::string& function,
@@ -3700,7 +3729,7 @@
   return "<unmatched texture overload>";
 }
 
-TEST_P(TypeDeterminerTextureIntrinsicTest, Call) {
+TEST_P(ResolverTextureIntrinsicTest, Call) {
   auto param = GetParam();
 
   param.buildTextureVariable(this);
@@ -3709,7 +3738,7 @@
   auto* call = Call(param.function, param.args(this));
   WrapInFunction(call);
 
-  ASSERT_TRUE(td()->Determine()) << td()->error();
+  ASSERT_TRUE(r()->Resolve()) << r()->error();
 
   if (std::string(param.function) == "textureDimensions") {
     switch (param.texture_dimension) {
diff --git a/src/transform/first_index_offset.cc b/src/transform/first_index_offset.cc
index cb543f8..6c2ecd0 100644
--- a/src/transform/first_index_offset.cc
+++ b/src/transform/first_index_offset.cc
@@ -53,7 +53,6 @@
 #include "src/semantic/function.h"
 #include "src/type/struct_type.h"
 #include "src/type/u32_type.h"
-#include "src/type_determiner.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::transform::FirstIndexOffset::Data);
 
diff --git a/src/transform/manager.cc b/src/transform/manager.cc
index e91a54f..e89f146 100644
--- a/src/transform/manager.cc
+++ b/src/transform/manager.cc
@@ -15,7 +15,6 @@
 #include "src/transform/manager.h"
 
 #include "src/program_builder.h"
-#include "src/type_determiner.h"
 
 namespace tint {
 namespace transform {
diff --git a/src/transform/test_helper.h b/src/transform/test_helper.h
index 191f1d8..3b062d2 100644
--- a/src/transform/test_helper.h
+++ b/src/transform/test_helper.h
@@ -25,7 +25,6 @@
 #include "src/program_builder.h"
 #include "src/reader/wgsl/parser.h"
 #include "src/transform/manager.h"
-#include "src/type_determiner.h"
 #include "src/writer/wgsl/generator.h"
 
 namespace tint {
diff --git a/src/type/storage_texture_type_test.cc b/src/type/storage_texture_type_test.cc
index bbacb47..22966f7 100644
--- a/src/type/storage_texture_type_test.cc
+++ b/src/type/storage_texture_type_test.cc
@@ -30,7 +30,6 @@
 #include "src/type/test_helper.h"
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
-#include "src/type_determiner.h"
 
 namespace tint {
 namespace type {
@@ -108,9 +107,10 @@
       type::StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, Types());
   Type* s = create<StorageTexture>(TextureDimension::k2dArray,
                                    ImageFormat::kRgba32Float, subtype);
-  TypeDeterminer td(this);
 
-  ASSERT_TRUE(td.Determine()) << td.error();
+  auto program = Build();
+
+  ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
   ASSERT_TRUE(s->Is<Texture>());
   ASSERT_TRUE(s->Is<StorageTexture>());
   EXPECT_TRUE(s->As<StorageTexture>()->type()->Is<F32>());
@@ -121,9 +121,10 @@
       type::StorageTexture::SubtypeFor(ImageFormat::kRg32Uint, Types());
   Type* s = create<StorageTexture>(TextureDimension::k2dArray,
                                    ImageFormat::kRg32Uint, subtype);
-  TypeDeterminer td(this);
 
-  ASSERT_TRUE(td.Determine()) << td.error();
+  auto program = Build();
+
+  ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
   ASSERT_TRUE(s->Is<Texture>());
   ASSERT_TRUE(s->Is<StorageTexture>());
   EXPECT_TRUE(s->As<StorageTexture>()->type()->Is<U32>());
@@ -134,9 +135,10 @@
       type::StorageTexture::SubtypeFor(ImageFormat::kRgba32Sint, Types());
   Type* s = create<StorageTexture>(TextureDimension::k2dArray,
                                    ImageFormat::kRgba32Sint, subtype);
-  TypeDeterminer td(this);
 
-  ASSERT_TRUE(td.Determine()) << td.error();
+  auto program = Build();
+
+  ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
   ASSERT_TRUE(s->Is<Texture>());
   ASSERT_TRUE(s->Is<StorageTexture>());
   EXPECT_TRUE(s->As<StorageTexture>()->type()->Is<I32>());
diff --git a/src/type/test_helper.h b/src/type/test_helper.h
index 0665100..e09e9c3 100644
--- a/src/type/test_helper.h
+++ b/src/type/test_helper.h
@@ -27,7 +27,19 @@
 
 /// Helper class for testing
 template <typename BASE>
-class TestHelperBase : public BASE, public ProgramBuilder {};
+class TestHelperBase : public BASE, public ProgramBuilder {
+ public:
+  /// Builds and returns the program. Must only be called once per test
+  /// @return the built program
+  Program Build() {
+    diag::Formatter formatter;
+    [&]() {
+      ASSERT_TRUE(IsValid()) << "Builder program is not valid\n"
+                             << formatter.format(Diagnostics());
+    }();
+    return Program(std::move(*this));
+  }
+};
 using TestHelper = TestHelperBase<testing::Test>;
 
 template <typename T>
diff --git a/src/validator/validator_control_block_test.cc b/src/validator/validator_control_block_test.cc
index 26fc506..047eede 100644
--- a/src/validator/validator_control_block_test.cc
+++ b/src/validator/validator_control_block_test.cc
@@ -27,7 +27,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/i32_type.h"
 #include "src/type/u32_type.h"
-#include "src/type_determiner.h"
 #include "src/validator/validator_impl.h"
 #include "src/validator/validator_test_helper.h"
 
diff --git a/src/validator/validator_function_test.cc b/src/validator/validator_function_test.cc
index 7870eb4..45133b0 100644
--- a/src/validator/validator_function_test.cc
+++ b/src/validator/validator_function_test.cc
@@ -25,7 +25,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/i32_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/validator/validator_impl.h"
 #include "src/validator/validator_test_helper.h"
 
diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc
index 8b38d07..e2aaef7 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -51,7 +51,6 @@
 #include "src/type/struct_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/validator/validator_impl.h"
 #include "src/validator/validator_test_helper.h"
 
@@ -85,39 +84,6 @@
             "reference storage: __i32");
 }
 
-TEST_F(ValidatorTest, UsingUndefinedVariable_Fail) {
-  // b = 2;
-
-  SetSource(Source{Source::Location{12, 34}});
-  auto* lhs = Expr("b");
-  auto* rhs = Expr(2);
-  auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
-  WrapInFunction(assign);
-
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(),
-            "12:34 error: v-0006: identifier must be declared before use: b");
-}
-
-TEST_F(ValidatorTest, UsingUndefinedVariableInBlockStatement_Fail) {
-  // {
-  //  b = 2;
-  // }
-
-  SetSource(Source{Source::Location{12, 34}});
-  auto* lhs = Expr("b");
-  auto* rhs = Expr(2);
-
-  auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::AssignmentStatement>(lhs, rhs),
-  });
-  WrapInFunction(body);
-
-  EXPECT_FALSE(td()->Determine());
-  EXPECT_EQ(td()->error(),
-            "12:34 error: v-0006: identifier must be declared before use: b");
-}
-
 TEST_F(ValidatorTest, AssignCompatibleTypes_Pass) {
   // var a :i32 = 2;
   // a = 2
diff --git a/src/validator/validator_test_helper.cc b/src/validator/validator_test_helper.cc
index 10dd3cd..df539ae 100644
--- a/src/validator/validator_test_helper.cc
+++ b/src/validator/validator_test_helper.cc
@@ -18,9 +18,7 @@
 
 namespace tint {
 
-ValidatorTestHelper::ValidatorTestHelper() {
-  td_ = std::make_unique<TypeDeterminer>(this);
-}
+ValidatorTestHelper::ValidatorTestHelper() = default;
 
 ValidatorTestHelper::~ValidatorTestHelper() = default;
 
diff --git a/src/validator/validator_test_helper.h b/src/validator/validator_test_helper.h
index 19d156c..e8297c2 100644
--- a/src/validator/validator_test_helper.h
+++ b/src/validator/validator_test_helper.h
@@ -24,7 +24,6 @@
 #include "src/program_builder.h"
 #include "src/semantic/expression.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/validator/validator_impl.h"
 
 namespace tint {
@@ -56,10 +55,6 @@
     return *val_;
   }
 
-  /// A handle to type_determiner
-  /// @returns a pointer to the type_determiner object
-  TypeDeterminer* td() const { return td_.get(); }
-
   /// Inserts a variable into the current scope.
   /// @param var the variable to register.
   void RegisterVariable(ast::Variable* var) {
@@ -78,7 +73,6 @@
   }
 
  private:
-  std::unique_ptr<TypeDeterminer> td_;
   std::unique_ptr<Program> program_;
   std::unique_ptr<ValidatorImpl> val_;
   std::vector<ast::Variable*> vars_for_testing_;
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 00b0c7a..82b5956 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
@@ -24,7 +24,6 @@
 #include "src/ast/stage_decoration.h"
 #include "src/ast/variable.h"
 #include "src/program.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/test_helper.h"
 
 namespace tint {
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index f60289c..92ee194 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -42,7 +42,6 @@
 #include "src/type/struct_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/test_helper.h"
 
 namespace tint {
diff --git a/src/writer/hlsl/generator_impl_import_test.cc b/src/writer/hlsl/generator_impl_import_test.cc
index ad27e40..c563e2e 100644
--- a/src/writer/hlsl/generator_impl_import_test.cc
+++ b/src/writer/hlsl/generator_impl_import_test.cc
@@ -27,7 +27,6 @@
 #include "src/type/i32_type.h"
 #include "src/type/matrix_type.h"
 #include "src/type/vector_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/test_helper.h"
 
 namespace tint {
diff --git a/src/writer/hlsl/generator_impl_intrinsic_test.cc b/src/writer/hlsl/generator_impl_intrinsic_test.cc
index d1353b9..db4fac1 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_test.cc
@@ -21,7 +21,6 @@
 #include "src/semantic/call.h"
 #include "src/type/f32_type.h"
 #include "src/type/vector_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/test_helper.h"
 
 namespace tint {
diff --git a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
index 99a7950..2d8b0c9 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
@@ -21,7 +21,6 @@
 #include "src/type/depth_texture_type.h"
 #include "src/type/multisampled_texture_type.h"
 #include "src/type/sampled_texture_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/generator_impl.h"
 #include "src/writer/hlsl/test_helper.h"
 
diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc
index c1ee1c5..2f3a1f3 100644
--- a/src/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -29,7 +29,6 @@
 #include "src/program.h"
 #include "src/type/struct_type.h"
 #include "src/type/vector_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/test_helper.h"
 
 namespace tint {
diff --git a/src/writer/hlsl/test_helper.h b/src/writer/hlsl/test_helper.h
index 3c6cff7..1d2db74 100644
--- a/src/writer/hlsl/test_helper.h
+++ b/src/writer/hlsl/test_helper.h
@@ -24,7 +24,6 @@
 #include "src/diagnostic/formatter.h"
 #include "src/program_builder.h"
 #include "src/transform/hlsl.h"
-#include "src/type_determiner.h"
 #include "src/writer/hlsl/generator_impl.h"
 
 namespace tint {
diff --git a/src/writer/msl/generator_impl_function_entry_point_data_test.cc b/src/writer/msl/generator_impl_function_entry_point_data_test.cc
index e2051aa..d78868b 100644
--- a/src/writer/msl/generator_impl_function_entry_point_data_test.cc
+++ b/src/writer/msl/generator_impl_function_entry_point_data_test.cc
@@ -27,7 +27,6 @@
 #include "src/type/i32_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
 
diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc
index 1c0d6db..f30f4db 100644
--- a/src/writer/msl/generator_impl_function_test.cc
+++ b/src/writer/msl/generator_impl_function_test.cc
@@ -44,7 +44,6 @@
 #include "src/type/struct_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
 
diff --git a/src/writer/msl/generator_impl_import_test.cc b/src/writer/msl/generator_impl_import_test.cc
index 5176a92..d0c9784 100644
--- a/src/writer/msl/generator_impl_import_test.cc
+++ b/src/writer/msl/generator_impl_import_test.cc
@@ -29,7 +29,6 @@
 #include "src/type/i32_type.h"
 #include "src/type/matrix_type.h"
 #include "src/type/vector_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
 
@@ -53,7 +52,7 @@
   auto param = GetParam();
   auto* call = Call(param.name, 1.f);
 
-  // The call type determination will set the intrinsic data for the ident
+  // The resolver will set the intrinsic data for the ident
   WrapInFunction(call);
 
   GeneratorImpl& gen = Build();
diff --git a/src/writer/msl/generator_impl_intrinsic_test.cc b/src/writer/msl/generator_impl_intrinsic_test.cc
index 7f26363..3960fa8 100644
--- a/src/writer/msl/generator_impl_intrinsic_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_test.cc
@@ -21,7 +21,6 @@
 #include "src/semantic/call.h"
 #include "src/type/f32_type.h"
 #include "src/type/vector_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
 
diff --git a/src/writer/msl/generator_impl_intrinsic_texture_test.cc b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
index 79b2f0c..767ec7c 100644
--- a/src/writer/msl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
@@ -19,7 +19,6 @@
 #include "src/type/depth_texture_type.h"
 #include "src/type/multisampled_texture_type.h"
 #include "src/type/sampled_texture_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
 
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc
index 9e441af..7aacb37 100644
--- a/src/writer/msl/generator_impl_type_test.cc
+++ b/src/writer/msl/generator_impl_type_test.cc
@@ -36,7 +36,6 @@
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 #include "src/writer/msl/test_helper.h"
 
diff --git a/src/writer/msl/test_helper.h b/src/writer/msl/test_helper.h
index bc3b77c..c0024d4 100644
--- a/src/writer/msl/test_helper.h
+++ b/src/writer/msl/test_helper.h
@@ -22,7 +22,6 @@
 #include "src/ast/module.h"
 #include "src/diagnostic/formatter.h"
 #include "src/program_builder.h"
-#include "src/type_determiner.h"
 #include "src/writer/msl/generator_impl.h"
 
 namespace tint {
diff --git a/src/writer/spirv/builder_accessor_expression_test.cc b/src/writer/spirv/builder_accessor_expression_test.cc
index 066afd1..6081637 100644
--- a/src/writer/spirv/builder_accessor_expression_test.cc
+++ b/src/writer/spirv/builder_accessor_expression_test.cc
@@ -33,7 +33,6 @@
 #include "src/type/struct_type.h"
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_assign_test.cc b/src/writer/spirv/builder_assign_test.cc
index 282a9b5..016e082 100644
--- a/src/writer/spirv/builder_assign_test.cc
+++ b/src/writer/spirv/builder_assign_test.cc
@@ -29,7 +29,6 @@
 #include "src/type/i32_type.h"
 #include "src/type/struct_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_binary_expression_test.cc b/src/writer/spirv/builder_binary_expression_test.cc
index 4414838..f5e0afc 100644
--- a/src/writer/spirv/builder_binary_expression_test.cc
+++ b/src/writer/spirv/builder_binary_expression_test.cc
@@ -29,7 +29,6 @@
 #include "src/type/matrix_type.h"
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_bitcast_expression_test.cc b/src/writer/spirv/builder_bitcast_expression_test.cc
index 4e8113f..a8e5423 100644
--- a/src/writer/spirv/builder_bitcast_expression_test.cc
+++ b/src/writer/spirv/builder_bitcast_expression_test.cc
@@ -19,7 +19,6 @@
 #include "src/program.h"
 #include "src/type/f32_type.h"
 #include "src/type/u32_type.h"
-#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"
diff --git a/src/writer/spirv/builder_block_test.cc b/src/writer/spirv/builder_block_test.cc
index ac6327b..04347b7 100644
--- a/src/writer/spirv/builder_block_test.cc
+++ b/src/writer/spirv/builder_block_test.cc
@@ -22,7 +22,6 @@
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/variable_decl_statement.h"
 #include "src/type/f32_type.h"
-#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"
diff --git a/src/writer/spirv/builder_call_test.cc b/src/writer/spirv/builder_call_test.cc
index 21fc547..eba20c6 100644
--- a/src/writer/spirv/builder_call_test.cc
+++ b/src/writer/spirv/builder_call_test.cc
@@ -27,7 +27,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/i32_type.h"
 #include "src/type/void_type.h"
-#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"
diff --git a/src/writer/spirv/builder_constructor_expression_test.cc b/src/writer/spirv/builder_constructor_expression_test.cc
index 54fd44c..d2a14ef 100644
--- a/src/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/writer/spirv/builder_constructor_expression_test.cc
@@ -37,7 +37,6 @@
 #include "src/type/struct_type.h"
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_discard_test.cc b/src/writer/spirv/builder_discard_test.cc
index 9445023..67abbe7 100644
--- a/src/writer/spirv/builder_discard_test.cc
+++ b/src/writer/spirv/builder_discard_test.cc
@@ -14,7 +14,6 @@
 
 #include "gtest/gtest.h"
 #include "src/ast/discard_statement.h"
-#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"
diff --git a/src/writer/spirv/builder_function_decoration_test.cc b/src/writer/spirv/builder_function_decoration_test.cc
index 13b8faf..a64dc69 100644
--- a/src/writer/spirv/builder_function_decoration_test.cc
+++ b/src/writer/spirv/builder_function_decoration_test.cc
@@ -25,7 +25,6 @@
 #include "src/ast/variable.h"
 #include "src/ast/workgroup_decoration.h"
 #include "src/semantic/function.h"
-#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"
diff --git a/src/writer/spirv/builder_function_test.cc b/src/writer/spirv/builder_function_test.cc
index 3e5803a..003298a 100644
--- a/src/writer/spirv/builder_function_test.cc
+++ b/src/writer/spirv/builder_function_test.cc
@@ -33,7 +33,6 @@
 #include "src/type/i32_type.h"
 #include "src/type/struct_type.h"
 #include "src/type/void_type.h"
-#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"
diff --git a/src/writer/spirv/builder_function_variable_test.cc b/src/writer/spirv/builder_function_variable_test.cc
index 8d716d1..d797882 100644
--- a/src/writer/spirv/builder_function_variable_test.cc
+++ b/src/writer/spirv/builder_function_variable_test.cc
@@ -32,7 +32,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/struct_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index f1833dd..07dd913 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -38,7 +38,6 @@
 #include "src/type/struct_type.h"
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_ident_expression_test.cc b/src/writer/spirv/builder_ident_expression_test.cc
index eb1fd13..4938f76 100644
--- a/src/writer/spirv/builder_ident_expression_test.cc
+++ b/src/writer/spirv/builder_ident_expression_test.cc
@@ -25,7 +25,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/i32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_if_test.cc b/src/writer/spirv/builder_if_test.cc
index b1e4c3f..dec8097 100644
--- a/src/writer/spirv/builder_if_test.cc
+++ b/src/writer/spirv/builder_if_test.cc
@@ -28,7 +28,6 @@
 #include "src/ast/sint_literal.h"
 #include "src/type/bool_type.h"
 #include "src/type/i32_type.h"
-#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"
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 613a5a0..c52c3e9 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -44,7 +44,6 @@
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#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"
diff --git a/src/writer/spirv/builder_intrinsic_texture_test.cc b/src/writer/spirv/builder_intrinsic_texture_test.cc
index b7deeb0..f569ea32 100644
--- a/src/writer/spirv/builder_intrinsic_texture_test.cc
+++ b/src/writer/spirv/builder_intrinsic_texture_test.cc
@@ -22,7 +22,6 @@
 #include "src/type/multisampled_texture_type.h"
 #include "src/type/sampled_texture_type.h"
 #include "src/type/storage_texture_type.h"
-#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"
diff --git a/src/writer/spirv/builder_loop_test.cc b/src/writer/spirv/builder_loop_test.cc
index 48ef0c0..662b449 100644
--- a/src/writer/spirv/builder_loop_test.cc
+++ b/src/writer/spirv/builder_loop_test.cc
@@ -23,7 +23,6 @@
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/sint_literal.h"
 #include "src/type/i32_type.h"
-#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"
diff --git a/src/writer/spirv/builder_return_test.cc b/src/writer/spirv/builder_return_test.cc
index 689769d..e4fd93d 100644
--- a/src/writer/spirv/builder_return_test.cc
+++ b/src/writer/spirv/builder_return_test.cc
@@ -22,7 +22,6 @@
 #include "src/ast/type_constructor_expression.h"
 #include "src/type/f32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/builder_switch_test.cc b/src/writer/spirv/builder_switch_test.cc
index 9dcf3da..67afa1b 100644
--- a/src/writer/spirv/builder_switch_test.cc
+++ b/src/writer/spirv/builder_switch_test.cc
@@ -27,7 +27,6 @@
 #include "src/ast/switch_statement.h"
 #include "src/type/bool_type.h"
 #include "src/type/i32_type.h"
-#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"
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index e66f1c1..06f534e 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -39,7 +39,6 @@
 #include "src/type/u32_type.h"
 #include "src/type/vector_type.h"
 #include "src/type/void_type.h"
-#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"
diff --git a/src/writer/spirv/builder_unary_op_expression_test.cc b/src/writer/spirv/builder_unary_op_expression_test.cc
index 4034dbf..8a6366a 100644
--- a/src/writer/spirv/builder_unary_op_expression_test.cc
+++ b/src/writer/spirv/builder_unary_op_expression_test.cc
@@ -25,7 +25,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/i32_type.h"
 #include "src/type/vector_type.h"
-#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"
diff --git a/src/writer/spirv/test_helper.h b/src/writer/spirv/test_helper.h
index bd053ab..0151016 100644
--- a/src/writer/spirv/test_helper.h
+++ b/src/writer/spirv/test_helper.h
@@ -25,7 +25,6 @@
 #include "src/diagnostic/formatter.h"
 #include "src/program_builder.h"
 #include "src/transform/spirv.h"
-#include "src/type_determiner.h"
 #include "src/writer/spirv/binary_writer.h"
 #include "src/writer/spirv/builder.h"
 
diff --git a/src/writer/wgsl/generator_impl_function_test.cc b/src/writer/wgsl/generator_impl_function_test.cc
index 0feacc8..52fceb4 100644
--- a/src/writer/wgsl/generator_impl_function_test.cc
+++ b/src/writer/wgsl/generator_impl_function_test.cc
@@ -29,7 +29,6 @@
 #include "src/type/f32_type.h"
 #include "src/type/i32_type.h"
 #include "src/type/void_type.h"
-#include "src/type_determiner.h"
 #include "src/writer/wgsl/generator_impl.h"
 #include "src/writer/wgsl/test_helper.h"
 
diff --git a/src/writer/wgsl/test_helper.h b/src/writer/wgsl/test_helper.h
index 5039ebe..074264f 100644
--- a/src/writer/wgsl/test_helper.h
+++ b/src/writer/wgsl/test_helper.h
@@ -20,7 +20,6 @@
 
 #include "gtest/gtest.h"
 #include "src/program_builder.h"
-#include "src/type_determiner.h"
 #include "src/writer/wgsl/generator_impl.h"
 
 namespace tint {
@@ -31,7 +30,7 @@
 template <typename BASE>
 class TestHelperBase : public BASE, public ProgramBuilder {
  public:
-  TestHelperBase() : td(this) {}
+  TestHelperBase() = default;
 
   ~TestHelperBase() override = default;
 
@@ -48,8 +47,6 @@
     return *gen_;
   }
 
-  /// The type determiner
-  TypeDeterminer td;
   /// The program built with a call to Build()
   std::unique_ptr<Program> program;