Prepare for having TypesBuilder return ast::Types

Instead of a typ::TypePair.

Bug: tint:724
Change-Id: Ife8fac86093eb128bc98974e1f6614a73c42a9e6
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49753
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
index bc923ca..3ff6156 100644
--- a/src/ast/intrinsic_texture_helper_test.cc
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -132,7 +132,7 @@
   return out;
 }
 
-ast::Type* TextureOverloadCase::resultVectorComponentType(
+ast::Type* TextureOverloadCase::buildResultVectorComponentType(
     ProgramBuilder* b) const {
   switch (texture_data_type) {
     case ast::intrinsic::test::TextureDataType::kF32:
@@ -149,8 +149,6 @@
 
 ast::Variable* TextureOverloadCase::buildTextureVariable(
     ProgramBuilder* b) const {
-  auto* datatype = resultVectorComponentType(b);
-
   DecorationList decos = {
       b->create<ast::GroupDecoration>(0),
       b->create<ast::BindingDecoration>(0),
@@ -158,7 +156,8 @@
   switch (texture_kind) {
     case ast::intrinsic::test::TextureKind::kRegular:
       return b->Global("texture",
-                       b->ty.sampled_texture(texture_dimension, datatype),
+                       b->ty.sampled_texture(texture_dimension,
+                                             buildResultVectorComponentType(b)),
                        ast::StorageClass::kUniformConstant, nullptr, decos);
 
     case ast::intrinsic::test::TextureKind::kDepth:
@@ -166,9 +165,11 @@
                        ast::StorageClass::kUniformConstant, nullptr, decos);
 
     case ast::intrinsic::test::TextureKind::kMultisampled:
-      return b->Global("texture",
-                       b->ty.multisampled_texture(texture_dimension, datatype),
-                       ast::StorageClass::kUniformConstant, nullptr, decos);
+      return b->Global(
+          "texture",
+          b->ty.multisampled_texture(texture_dimension,
+                                     buildResultVectorComponentType(b)),
+          ast::StorageClass::kUniformConstant, nullptr, decos);
 
     case ast::intrinsic::test::TextureKind::kStorage: {
       auto st = b->ty.storage_texture(texture_dimension, image_format);
diff --git a/src/ast/intrinsic_texture_helper_test.h b/src/ast/intrinsic_texture_helper_test.h
index 0180efd..2151e47 100644
--- a/src/ast/intrinsic_texture_helper_test.h
+++ b/src/ast/intrinsic_texture_helper_test.h
@@ -209,7 +209,7 @@
 
   /// @param builder the AST builder used for the test
   /// @returns the vector component type of the texture function return value
-  ast::Type* resultVectorComponentType(ProgramBuilder* builder) const;
+  ast::Type* buildResultVectorComponentType(ProgramBuilder* builder) const;
   /// @param builder the AST builder used for the test
   /// @returns a variable holding the test texture, automatically registered as
   /// a global variable.
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index be82b9a..b4151aa 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3209,7 +3209,7 @@
         return true;
       }
       auto expr = MakeExpression(value_id);
-      if (!expr.type || !expr.expr) {
+      if (!expr.type.ast || !expr.expr) {
         return false;
       }
       expr.type = RemapStorageClass(expr.type, result_id);
@@ -3686,7 +3686,7 @@
     const auto pointer_type_id =
         type_mgr_->FindPointerToType(pointee_type_id, storage_class);
     auto ast_pointer_type = parser_impl_.ConvertType(pointer_type_id);
-    TINT_ASSERT(ast_pointer_type);
+    TINT_ASSERT(ast_pointer_type.ast);
     TINT_ASSERT(ast_pointer_type.ast->Is<ast::Pointer>());
     current_expr = TypedExpression{ast_pointer_type, next_expr};
   }
@@ -4276,7 +4276,7 @@
   auto* call_expr =
       create<ast::CallExpression>(Source{}, function, std::move(params));
   auto result_type = parser_impl_.ConvertType(inst.type_id());
-  if (!result_type) {
+  if (!result_type.ast) {
     return Fail() << "internal error: no mapped type result of call: "
                   << inst.PrettyPrint();
   }
@@ -4355,7 +4355,7 @@
   auto* call_expr =
       create<ast::CallExpression>(Source{}, ident, std::move(params));
   auto result_type = parser_impl_.ConvertType(inst.type_id());
-  if (!result_type) {
+  if (!result_type.ast) {
     Fail() << "internal error: no mapped type result of call: "
            << inst.PrettyPrint();
     return {};
@@ -4482,13 +4482,13 @@
   }
 
   typ::Pointer texture_ptr_type = parser_impl_.GetTypeForHandleVar(*image);
-  if (!texture_ptr_type) {
+  if (!texture_ptr_type.ast) {
     return Fail();
   }
   typ::Texture texture_type =
       typ::As<typ::Texture>(UnwrapAll(typ::Call_type(texture_ptr_type)));
 
-  if (!texture_type) {
+  if (!texture_type.ast) {
     return Fail();
   }
 
@@ -4908,13 +4908,13 @@
     typ::Texture texture_type) {
   auto storage_texture_type = typ::As<typ::StorageTexture>(texture_type);
   auto src_type = texel.type;
-  if (!storage_texture_type) {
+  if (!storage_texture_type.ast) {
     Fail() << "writing to other than storage texture: " << inst.PrettyPrint();
     return nullptr;
   }
-  const auto format = storage_texture_type->image_format();
+  const auto format = storage_texture_type.ast->image_format();
   auto dest_type = parser_impl_.GetTexelTypeForFormat(format);
-  if (!dest_type) {
+  if (!dest_type.ast) {
     Fail();
     return nullptr;
   }
@@ -4923,14 +4923,14 @@
   }
 
   const uint32_t dest_count =
-      dest_type->is_scalar() ? 1 : dest_type.ast->As<ast::Vector>()->size();
+      dest_type.ast->is_scalar() ? 1 : dest_type.ast->As<ast::Vector>()->size();
   if (dest_count == 3) {
     Fail() << "3-channel storage textures are not supported: "
            << inst.PrettyPrint();
     return nullptr;
   }
   const uint32_t src_count =
-      src_type->is_scalar() ? 1 : src_type.ast->As<ast::Vector>()->size();
+      src_type.ast->is_scalar() ? 1 : src_type.ast->As<ast::Vector>()->size();
   if (src_count < dest_count) {
     Fail() << "texel has too few components for storage texture: " << src_count
            << " provided but " << dest_count
@@ -4945,29 +4945,29 @@
           : create<ast::MemberAccessorExpression>(Source{}, texel.expr,
                                                   PrefixSwizzle(dest_count));
 
-  if (!(dest_type->is_float_scalar_or_vector() ||
-        dest_type->is_unsigned_scalar_or_vector() ||
-        dest_type->is_signed_scalar_or_vector())) {
+  if (!(dest_type.ast->is_float_scalar_or_vector() ||
+        dest_type.ast->is_unsigned_scalar_or_vector() ||
+        dest_type.ast->is_signed_scalar_or_vector())) {
     Fail() << "invalid destination type for storage texture write: "
-           << dest_type->type_name();
+           << dest_type.ast->type_name();
     return nullptr;
   }
-  if (!(src_type->is_float_scalar_or_vector() ||
-        src_type->is_unsigned_scalar_or_vector() ||
-        src_type->is_signed_scalar_or_vector())) {
+  if (!(src_type.ast->is_float_scalar_or_vector() ||
+        src_type.ast->is_unsigned_scalar_or_vector() ||
+        src_type.ast->is_signed_scalar_or_vector())) {
     Fail() << "invalid texel type for storage texture write: "
            << inst.PrettyPrint();
     return nullptr;
   }
-  if (dest_type->is_float_scalar_or_vector() &&
-      !src_type->is_float_scalar_or_vector()) {
+  if (dest_type.ast->is_float_scalar_or_vector() &&
+      !src_type.ast->is_float_scalar_or_vector()) {
     Fail() << "can only write float or float vector to a storage image with "
               "floating texel format: "
            << inst.PrettyPrint();
     return nullptr;
   }
-  if (!dest_type->is_float_scalar_or_vector() &&
-      src_type->is_float_scalar_or_vector()) {
+  if (!dest_type.ast->is_float_scalar_or_vector() &&
+      src_type.ast->is_float_scalar_or_vector()) {
     Fail()
         << "float or float vector can only be written to a storage image with "
            "floating texel format: "
@@ -4975,13 +4975,13 @@
     return nullptr;
   }
 
-  if (dest_type->is_float_scalar_or_vector()) {
+  if (dest_type.ast->is_float_scalar_or_vector()) {
     return texel_prefix;
   }
   // The only remaining cases are signed/unsigned source, and signed/unsigned
   // destination.
-  if (dest_type->is_unsigned_scalar_or_vector() ==
-      src_type->is_unsigned_scalar_or_vector()) {
+  if (dest_type.ast->is_unsigned_scalar_or_vector() ==
+      src_type.ast->is_unsigned_scalar_or_vector()) {
     return texel_prefix;
   }
   // We must do a bitcast conversion.
diff --git a/src/reader/spirv/function_arithmetic_test.cc b/src/reader/spirv/function_arithmetic_test.cc
index 4e8ee20..1172379 100644
--- a/src/reader/spirv/function_arithmetic_test.cc
+++ b/src/reader/spirv/function_arithmetic_test.cc
@@ -145,7 +145,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -171,7 +173,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -199,7 +203,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -227,7 +233,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -257,7 +265,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -287,7 +297,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -319,7 +331,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -351,7 +365,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -385,7 +401,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -411,7 +429,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(
@@ -1221,7 +1241,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1249,7 +1270,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1277,7 +1299,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1305,7 +1328,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1333,7 +1357,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1361,7 +1386,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1393,7 +1419,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   auto got = ToString(p->builder(), fe.ast_body());
@@ -1519,7 +1546,8 @@
      OpFunctionEnd
 )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << assembly << p->error();
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
@@ -1565,7 +1593,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   const auto* expected = R"(
@@ -1598,7 +1628,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   // Note, in the AST dump mat_2_3 means 2 rows and 3 columns.
@@ -1634,7 +1666,9 @@
      OpFunctionEnd
   )";
   auto p = parser(test::Assemble(assembly));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
   auto fe = p->function_emitter(100);
   EXPECT_TRUE(fe.EmitBody()) << p->error();
   const auto* expected = R"(
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index ce972fa..2077927 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -328,7 +328,7 @@
 
   auto maybe_generate_alias = [this, type_id,
                                spirv_type](typ::Type type) -> typ::Type {
-    if (type != nullptr) {
+    if (type.ast != nullptr) {
       return MaybeGenerateAlias(type_id, spirv_type, type);
     }
     return {};
@@ -802,7 +802,7 @@
     const spvtools::opt::analysis::Vector* vec_ty) {
   const auto num_elem = vec_ty->element_count();
   auto ast_elem_ty = ConvertType(type_mgr_->GetId(vec_ty->element_type()));
-  if (ast_elem_ty == nullptr) {
+  if (ast_elem_ty.ast == nullptr) {
     return nullptr;
   }
   return builder_.ty.vec(ast_elem_ty, num_elem);
@@ -815,7 +815,7 @@
   const auto num_rows = vec_ty->element_count();
   const auto num_columns = mat_ty->element_count();
   auto ast_scalar_ty = ConvertType(type_mgr_->GetId(scalar_ty));
-  if (ast_scalar_ty == nullptr) {
+  if (ast_scalar_ty.ast == nullptr) {
     return nullptr;
   }
   return builder_.ty.mat(ast_scalar_ty, num_columns, num_rows);
@@ -824,7 +824,7 @@
 typ::Type ParserImpl::ConvertType(
     const spvtools::opt::analysis::RuntimeArray* rtarr_ty) {
   auto ast_elem_ty = ConvertType(type_mgr_->GetId(rtarr_ty->element_type()));
-  if (ast_elem_ty == nullptr) {
+  if (ast_elem_ty.ast == nullptr) {
     return nullptr;
   }
   ast::DecorationList decorations;
@@ -838,7 +838,7 @@
     const spvtools::opt::analysis::Array* arr_ty) {
   const auto elem_type_id = type_mgr_->GetId(arr_ty->element_type());
   auto ast_elem_ty = ConvertType(elem_type_id);
-  if (ast_elem_ty == nullptr) {
+  if (ast_elem_ty.ast == nullptr) {
     return nullptr;
   }
   const auto& length_info = arr_ty->length_info();
@@ -940,7 +940,7 @@
        ++member_index) {
     const auto member_type_id = type_mgr_->GetId(members[member_index]);
     auto ast_member_ty = ConvertType(member_type_id);
-    if (ast_member_ty == nullptr) {
+    if (ast_member_ty.ast == nullptr) {
       // Already emitted diagnostics.
       return nullptr;
     }
@@ -1058,7 +1058,7 @@
     return nullptr;
   }
   auto ast_elem_ty = ConvertType(pointee_type_id);
-  if (ast_elem_ty == nullptr) {
+  if (ast_elem_ty.ast == nullptr) {
     Fail() << "SPIR-V pointer type with ID " << type_id
            << " has invalid pointee type " << pointee_type_id;
     return nullptr;
@@ -1206,7 +1206,7 @@
       return ast_type;
   }
   auto ast_underlying_type = ast_type;
-  if (ast_underlying_type == nullptr) {
+  if (ast_underlying_type.ast == nullptr) {
     Fail() << "internal error: no type registered for SPIR-V ID: " << type_id;
     return {};
   }
@@ -1267,7 +1267,7 @@
       }
     } else {
       ast_type = ConvertType(type_id);
-      if (ast_type == nullptr) {
+      if (ast_type.ast == nullptr) {
         return Fail() << "internal error: failed to register Tint AST type for "
                          "SPIR-V type with ID: "
                       << var.type_id();
@@ -1346,7 +1346,7 @@
                                         bool is_const,
                                         ast::Expression* constructor,
                                         ast::DecorationList decorations) {
-  if (type == nullptr) {
+  if (type.ast == nullptr) {
     Fail() << "internal error: can't make ast::Variable for null type";
     return nullptr;
   }
@@ -1459,7 +1459,7 @@
     return {};
   }
   auto original_ast_type = ConvertType(inst->type_id());
-  if (original_ast_type == nullptr) {
+  if (original_ast_type.ast == nullptr) {
     return {};
   }
 
@@ -1671,7 +1671,7 @@
     return {};
   }
   auto type = expr.type;
-  if (!type) {
+  if (!type.ast) {
     Fail() << "internal error: unmapped type for: " << builder_.str(expr.expr)
            << "\n";
     return {};
@@ -1725,7 +1725,7 @@
 }
 
 typ::Type ParserImpl::GetSignedIntMatchingShape(typ::Type other) {
-  if (other == nullptr) {
+  if (other.ast == nullptr) {
     Fail() << "no type provided";
   }
   auto i32 = builder_.ty.i32();
@@ -1742,7 +1742,7 @@
 }
 
 typ::Type ParserImpl::GetUnsignedIntMatchingShape(typ::Type other) {
-  if (other == nullptr) {
+  if (other.ast == nullptr) {
     Fail() << "no type provided";
     return nullptr;
   }
@@ -1764,7 +1764,7 @@
     const spvtools::opt::Instruction& inst,
     typ::Type first_operand_type) {
   auto forced_result_ty = ForcedResultType(inst, first_operand_type);
-  if ((forced_result_ty == nullptr) || (forced_result_ty == expr.type)) {
+  if ((forced_result_ty.ast == nullptr) || (forced_result_ty == expr.type)) {
     return expr;
   }
   return {expr.type,
diff --git a/src/reader/spirv/parser_impl_convert_type_test.cc b/src/reader/spirv/parser_impl_convert_type_test.cc
index 2249277..319e8a6 100644
--- a/src/reader/spirv/parser_impl_convert_type_test.cc
+++ b/src/reader/spirv/parser_impl_convert_type_test.cc
@@ -26,14 +26,14 @@
 TEST_F(SpvParserTest, ConvertType_PreservesExistingFailure) {
   auto p = parser(std::vector<uint32_t>{});
   p->Fail() << "boing";
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(), Eq("boing"));
 }
 
 TEST_F(SpvParserTest, ConvertType_RequiresInternalRepresntation) {
   auto p = parser(std::vector<uint32_t>{});
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(
       p->error(),
@@ -44,7 +44,7 @@
   auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\""));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_EQ(nullptr, type);
   EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 10"));
@@ -54,7 +54,7 @@
   auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\""));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(1);
+  auto type = p->ConvertType(1);
   EXPECT_EQ(nullptr, type);
   EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 1"));
 }
@@ -64,7 +64,7 @@
   auto p = parser(test::Assemble("%70 = OpTypePipe WriteOnly"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(70);
+  auto type = p->ConvertType(70);
   EXPECT_EQ(nullptr, type);
   EXPECT_THAT(p->error(),
               Eq("unknown SPIR-V type with ID 70: %70 = OpTypePipe WriteOnly"));
@@ -74,8 +74,8 @@
   auto p = parser(test::Assemble("%1 = OpTypeVoid"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(1);
-  EXPECT_TRUE(type->Is<sem::Void>());
+  auto type = p->ConvertType(1);
+  EXPECT_TRUE(type.ast->Is<ast::Void>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -83,8 +83,8 @@
   auto p = parser(test::Assemble("%100 = OpTypeBool"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(100);
-  EXPECT_TRUE(type->Is<sem::Bool>());
+  auto type = p->ConvertType(100);
+  EXPECT_TRUE(type.ast->Is<ast::Bool>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -92,8 +92,8 @@
   auto p = parser(test::Assemble("%2 = OpTypeInt 32 1"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(2);
-  EXPECT_TRUE(type->Is<sem::I32>());
+  auto type = p->ConvertType(2);
+  EXPECT_TRUE(type.ast->Is<ast::I32>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -101,8 +101,8 @@
   auto p = parser(test::Assemble("%3 = OpTypeInt 32 0"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::U32>());
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::U32>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -110,8 +110,8 @@
   auto p = parser(test::Assemble("%4 = OpTypeFloat 32"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(4);
-  EXPECT_TRUE(type->Is<sem::F32>());
+  auto type = p->ConvertType(4);
+  EXPECT_TRUE(type.ast->Is<ast::F32>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -119,7 +119,7 @@
   auto p = parser(test::Assemble("%5 = OpTypeInt 17 1"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(5);
+  auto type = p->ConvertType(5);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(), Eq("unhandled integer width: 17"));
 }
@@ -128,7 +128,7 @@
   auto p = parser(test::Assemble("%6 = OpTypeFloat 19"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(6);
+  auto type = p->ConvertType(6);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(), Eq("unhandled float width: 19"));
 }
@@ -140,7 +140,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(20);
+  auto type = p->ConvertType(20);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5"));
 }
@@ -154,20 +154,20 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* v2xf32 = p->ConvertType(20);
-  EXPECT_TRUE(v2xf32->Is<sem::Vector>());
-  EXPECT_TRUE(v2xf32->As<sem::Vector>()->type()->Is<sem::F32>());
-  EXPECT_EQ(v2xf32->As<sem::Vector>()->size(), 2u);
+  auto v2xf32 = p->ConvertType(20);
+  EXPECT_TRUE(v2xf32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v2xf32.ast->As<ast::Vector>()->type()->Is<ast::F32>());
+  EXPECT_EQ(v2xf32.ast->As<ast::Vector>()->size(), 2u);
 
-  auto* v3xf32 = p->ConvertType(30);
-  EXPECT_TRUE(v3xf32->Is<sem::Vector>());
-  EXPECT_TRUE(v3xf32->As<sem::Vector>()->type()->Is<sem::F32>());
-  EXPECT_EQ(v3xf32->As<sem::Vector>()->size(), 3u);
+  auto v3xf32 = p->ConvertType(30);
+  EXPECT_TRUE(v3xf32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v3xf32.ast->As<ast::Vector>()->type()->Is<ast::F32>());
+  EXPECT_EQ(v3xf32.ast->As<ast::Vector>()->size(), 3u);
 
-  auto* v4xf32 = p->ConvertType(40);
-  EXPECT_TRUE(v4xf32->Is<sem::Vector>());
-  EXPECT_TRUE(v4xf32->As<sem::Vector>()->type()->Is<sem::F32>());
-  EXPECT_EQ(v4xf32->As<sem::Vector>()->size(), 4u);
+  auto v4xf32 = p->ConvertType(40);
+  EXPECT_TRUE(v4xf32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v4xf32.ast->As<ast::Vector>()->type()->Is<ast::F32>());
+  EXPECT_EQ(v4xf32.ast->As<ast::Vector>()->size(), 4u);
 
   EXPECT_TRUE(p->error().empty());
 }
@@ -181,20 +181,20 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* v2xi32 = p->ConvertType(20);
-  EXPECT_TRUE(v2xi32->Is<sem::Vector>());
-  EXPECT_TRUE(v2xi32->As<sem::Vector>()->type()->Is<sem::I32>());
-  EXPECT_EQ(v2xi32->As<sem::Vector>()->size(), 2u);
+  auto v2xi32 = p->ConvertType(20);
+  EXPECT_TRUE(v2xi32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v2xi32.ast->As<ast::Vector>()->type()->Is<ast::I32>());
+  EXPECT_EQ(v2xi32.ast->As<ast::Vector>()->size(), 2u);
 
-  auto* v3xi32 = p->ConvertType(30);
-  EXPECT_TRUE(v3xi32->Is<sem::Vector>());
-  EXPECT_TRUE(v3xi32->As<sem::Vector>()->type()->Is<sem::I32>());
-  EXPECT_EQ(v3xi32->As<sem::Vector>()->size(), 3u);
+  auto v3xi32 = p->ConvertType(30);
+  EXPECT_TRUE(v3xi32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v3xi32.ast->As<ast::Vector>()->type()->Is<ast::I32>());
+  EXPECT_EQ(v3xi32.ast->As<ast::Vector>()->size(), 3u);
 
-  auto* v4xi32 = p->ConvertType(40);
-  EXPECT_TRUE(v4xi32->Is<sem::Vector>());
-  EXPECT_TRUE(v4xi32->As<sem::Vector>()->type()->Is<sem::I32>());
-  EXPECT_EQ(v4xi32->As<sem::Vector>()->size(), 4u);
+  auto v4xi32 = p->ConvertType(40);
+  EXPECT_TRUE(v4xi32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v4xi32.ast->As<ast::Vector>()->type()->Is<ast::I32>());
+  EXPECT_EQ(v4xi32.ast->As<ast::Vector>()->size(), 4u);
 
   EXPECT_TRUE(p->error().empty());
 }
@@ -208,20 +208,20 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* v2xu32 = p->ConvertType(20);
-  EXPECT_TRUE(v2xu32->Is<sem::Vector>());
-  EXPECT_TRUE(v2xu32->As<sem::Vector>()->type()->Is<sem::U32>());
-  EXPECT_EQ(v2xu32->As<sem::Vector>()->size(), 2u);
+  auto v2xu32 = p->ConvertType(20);
+  EXPECT_TRUE(v2xu32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v2xu32.ast->As<ast::Vector>()->type()->Is<ast::U32>());
+  EXPECT_EQ(v2xu32.ast->As<ast::Vector>()->size(), 2u);
 
-  auto* v3xu32 = p->ConvertType(30);
-  EXPECT_TRUE(v3xu32->Is<sem::Vector>());
-  EXPECT_TRUE(v3xu32->As<sem::Vector>()->type()->Is<sem::U32>());
-  EXPECT_EQ(v3xu32->As<sem::Vector>()->size(), 3u);
+  auto v3xu32 = p->ConvertType(30);
+  EXPECT_TRUE(v3xu32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v3xu32.ast->As<ast::Vector>()->type()->Is<ast::U32>());
+  EXPECT_EQ(v3xu32.ast->As<ast::Vector>()->size(), 3u);
 
-  auto* v4xu32 = p->ConvertType(40);
-  EXPECT_TRUE(v4xu32->Is<sem::Vector>());
-  EXPECT_TRUE(v4xu32->As<sem::Vector>()->type()->Is<sem::U32>());
-  EXPECT_EQ(v4xu32->As<sem::Vector>()->size(), 4u);
+  auto v4xu32 = p->ConvertType(40);
+  EXPECT_TRUE(v4xu32.ast->Is<ast::Vector>());
+  EXPECT_TRUE(v4xu32.ast->As<ast::Vector>()->type()->Is<ast::U32>());
+  EXPECT_EQ(v4xu32.ast->As<ast::Vector>()->size(), 4u);
 
   EXPECT_TRUE(p->error().empty());
 }
@@ -234,7 +234,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(20);
+  auto type = p->ConvertType(20);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5"));
 }
@@ -260,59 +260,59 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* m22 = p->ConvertType(22);
-  EXPECT_TRUE(m22->Is<sem::Matrix>());
-  EXPECT_TRUE(m22->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m22->As<sem::Matrix>()->rows(), 2u);
-  EXPECT_EQ(m22->As<sem::Matrix>()->columns(), 2u);
+  auto m22 = p->ConvertType(22);
+  EXPECT_TRUE(m22.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m22.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m22.ast->As<ast::Matrix>()->rows(), 2u);
+  EXPECT_EQ(m22.ast->As<ast::Matrix>()->columns(), 2u);
 
-  auto* m23 = p->ConvertType(23);
-  EXPECT_TRUE(m23->Is<sem::Matrix>());
-  EXPECT_TRUE(m23->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m23->As<sem::Matrix>()->rows(), 2u);
-  EXPECT_EQ(m23->As<sem::Matrix>()->columns(), 3u);
+  auto m23 = p->ConvertType(23);
+  EXPECT_TRUE(m23.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m23.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m23.ast->As<ast::Matrix>()->rows(), 2u);
+  EXPECT_EQ(m23.ast->As<ast::Matrix>()->columns(), 3u);
 
-  auto* m24 = p->ConvertType(24);
-  EXPECT_TRUE(m24->Is<sem::Matrix>());
-  EXPECT_TRUE(m24->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m24->As<sem::Matrix>()->rows(), 2u);
-  EXPECT_EQ(m24->As<sem::Matrix>()->columns(), 4u);
+  auto m24 = p->ConvertType(24);
+  EXPECT_TRUE(m24.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m24.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m24.ast->As<ast::Matrix>()->rows(), 2u);
+  EXPECT_EQ(m24.ast->As<ast::Matrix>()->columns(), 4u);
 
-  auto* m32 = p->ConvertType(32);
-  EXPECT_TRUE(m32->Is<sem::Matrix>());
-  EXPECT_TRUE(m32->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m32->As<sem::Matrix>()->rows(), 3u);
-  EXPECT_EQ(m32->As<sem::Matrix>()->columns(), 2u);
+  auto m32 = p->ConvertType(32);
+  EXPECT_TRUE(m32.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m32.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m32.ast->As<ast::Matrix>()->rows(), 3u);
+  EXPECT_EQ(m32.ast->As<ast::Matrix>()->columns(), 2u);
 
-  auto* m33 = p->ConvertType(33);
-  EXPECT_TRUE(m33->Is<sem::Matrix>());
-  EXPECT_TRUE(m33->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m33->As<sem::Matrix>()->rows(), 3u);
-  EXPECT_EQ(m33->As<sem::Matrix>()->columns(), 3u);
+  auto m33 = p->ConvertType(33);
+  EXPECT_TRUE(m33.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m33.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m33.ast->As<ast::Matrix>()->rows(), 3u);
+  EXPECT_EQ(m33.ast->As<ast::Matrix>()->columns(), 3u);
 
-  auto* m34 = p->ConvertType(34);
-  EXPECT_TRUE(m34->Is<sem::Matrix>());
-  EXPECT_TRUE(m34->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m34->As<sem::Matrix>()->rows(), 3u);
-  EXPECT_EQ(m34->As<sem::Matrix>()->columns(), 4u);
+  auto m34 = p->ConvertType(34);
+  EXPECT_TRUE(m34.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m34.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m34.ast->As<ast::Matrix>()->rows(), 3u);
+  EXPECT_EQ(m34.ast->As<ast::Matrix>()->columns(), 4u);
 
-  auto* m42 = p->ConvertType(42);
-  EXPECT_TRUE(m42->Is<sem::Matrix>());
-  EXPECT_TRUE(m42->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m42->As<sem::Matrix>()->rows(), 4u);
-  EXPECT_EQ(m42->As<sem::Matrix>()->columns(), 2u);
+  auto m42 = p->ConvertType(42);
+  EXPECT_TRUE(m42.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m42.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m42.ast->As<ast::Matrix>()->rows(), 4u);
+  EXPECT_EQ(m42.ast->As<ast::Matrix>()->columns(), 2u);
 
-  auto* m43 = p->ConvertType(43);
-  EXPECT_TRUE(m43->Is<sem::Matrix>());
-  EXPECT_TRUE(m43->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m43->As<sem::Matrix>()->rows(), 4u);
-  EXPECT_EQ(m43->As<sem::Matrix>()->columns(), 3u);
+  auto m43 = p->ConvertType(43);
+  EXPECT_TRUE(m43.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m43.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m43.ast->As<ast::Matrix>()->rows(), 4u);
+  EXPECT_EQ(m43.ast->As<ast::Matrix>()->columns(), 3u);
 
-  auto* m44 = p->ConvertType(44);
-  EXPECT_TRUE(m44->Is<sem::Matrix>());
-  EXPECT_TRUE(m44->As<sem::Matrix>()->type()->Is<sem::F32>());
-  EXPECT_EQ(m44->As<sem::Matrix>()->rows(), 4u);
-  EXPECT_EQ(m44->As<sem::Matrix>()->columns(), 4u);
+  auto m44 = p->ConvertType(44);
+  EXPECT_TRUE(m44.ast->Is<ast::Matrix>());
+  EXPECT_TRUE(m44.ast->As<ast::Matrix>()->type()->Is<ast::F32>());
+  EXPECT_EQ(m44.ast->As<ast::Matrix>()->rows(), 4u);
+  EXPECT_EQ(m44.ast->As<ast::Matrix>()->columns(), 4u);
 
   EXPECT_TRUE(p->error().empty());
 }
@@ -324,17 +324,17 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->UnwrapAliasIfNeeded()->Is<sem::ArrayType>());
-  auto* arr_type = type->UnwrapAliasIfNeeded()->As<sem::ArrayType>();
+  EXPECT_TRUE(type.ast->UnwrapAliasIfNeeded()->Is<ast::Array>());
+  auto* arr_type = type.ast->UnwrapAliasIfNeeded()->As<ast::Array>();
   EXPECT_TRUE(arr_type->IsRuntimeArray());
   ASSERT_NE(arr_type, nullptr);
   EXPECT_EQ(arr_type->size(), 0u);
   EXPECT_EQ(arr_type->decorations().size(), 0u);
   auto* elem_type = arr_type->type();
   ASSERT_NE(elem_type, nullptr);
-  EXPECT_TRUE(elem_type->Is<sem::U32>());
+  EXPECT_TRUE(elem_type->Is<ast::U32>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -345,7 +345,7 @@
     %10 = OpTypeRuntimeArray %uint
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(
       p->error(),
@@ -359,9 +359,9 @@
     %10 = OpTypeRuntimeArray %uint
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  auto* arr_type = type->UnwrapAliasIfNeeded()->As<sem::ArrayType>();
+  auto* arr_type = type.ast->UnwrapAliasIfNeeded()->As<ast::Array>();
   EXPECT_TRUE(arr_type->IsRuntimeArray());
   ASSERT_NE(arr_type, nullptr);
   ASSERT_EQ(arr_type->decorations().size(), 1u);
@@ -378,7 +378,7 @@
     %10 = OpTypeRuntimeArray %uint
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("invalid array type ID 10: ArrayStride can't be 0"));
@@ -393,7 +393,7 @@
     %10 = OpTypeRuntimeArray %uint
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("invalid array type ID 10: multiple ArrayStride decorations"));
@@ -407,17 +407,17 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->Is<sem::ArrayType>());
-  auto* arr_type = type->As<sem::ArrayType>();
+  EXPECT_TRUE(type.ast->Is<ast::Array>());
+  auto* arr_type = type.ast->As<ast::Array>();
   EXPECT_FALSE(arr_type->IsRuntimeArray());
   ASSERT_NE(arr_type, nullptr);
   EXPECT_EQ(arr_type->size(), 42u);
   EXPECT_EQ(arr_type->decorations().size(), 0u);
   auto* elem_type = arr_type->type();
   ASSERT_NE(elem_type, nullptr);
-  EXPECT_TRUE(elem_type->Is<sem::U32>());
+  EXPECT_TRUE(elem_type->Is<ast::U32>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -430,7 +430,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("Array type 10 length is a specialization constant"));
@@ -445,7 +445,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("Array type 10 length is a specialization constant"));
@@ -463,7 +463,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_EQ(type, nullptr);
   // TODO(dneto): Right now it's rejected earlier in the flow because
   // we can't even utter the uint64 type.
@@ -478,7 +478,7 @@
     %10 = OpTypeArray %uint %uint_5
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(
       p->error(),
@@ -494,10 +494,10 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->UnwrapAliasIfNeeded()->Is<sem::ArrayType>());
-  auto* arr_type = type->UnwrapAliasIfNeeded()->As<sem::ArrayType>();
+  EXPECT_TRUE(type.ast->UnwrapAliasIfNeeded()->Is<ast::Array>());
+  auto* arr_type = type.ast->UnwrapAliasIfNeeded()->As<ast::Array>();
   ASSERT_NE(arr_type, nullptr);
 
   ASSERT_EQ(arr_type->decorations().size(), 1u);
@@ -517,7 +517,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("invalid array type ID 10: ArrayStride can't be 0"));
@@ -533,7 +533,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("invalid array type ID 10: multiple ArrayStride decorations"));
@@ -548,12 +548,12 @@
   EXPECT_TRUE(p->BuildInternalModule());
   EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->Is<sem::StructType>());
+  EXPECT_TRUE(type.ast->Is<ast::Struct>());
 
   Program program = p->program();
-  EXPECT_THAT(program.str(type->As<sem::StructType>()->impl()), Eq(R"(Struct S {
+  EXPECT_THAT(program.str(type.ast->As<ast::Struct>()), Eq(R"(Struct S {
   StructMember{field0: __u32}
   StructMember{field1: __f32}
 }
@@ -569,12 +569,12 @@
   EXPECT_TRUE(p->BuildInternalModule());
   EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->Is<sem::StructType>());
+  EXPECT_TRUE(type.ast->Is<ast::Struct>());
 
   Program program = p->program();
-  EXPECT_THAT(program.str(type->As<sem::StructType>()->impl()), Eq(R"(Struct S {
+  EXPECT_THAT(program.str(type.ast->As<ast::Struct>()), Eq(R"(Struct S {
   [[block]]
   StructMember{field0: __u32}
 }
@@ -594,12 +594,12 @@
   EXPECT_TRUE(p->BuildInternalModule());
   EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
 
-  auto* type = p->ConvertType(10);
+  auto type = p->ConvertType(10);
   ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->Is<sem::StructType>());
+  EXPECT_TRUE(type.ast->Is<ast::Struct>());
 
   Program program = p->program();
-  EXPECT_THAT(program.str(type->As<sem::StructType>()->impl()), Eq(R"(Struct S {
+  EXPECT_THAT(program.str(type.ast->As<ast::Struct>()), Eq(R"(Struct S {
   StructMember{[[ offset 0 ]] field0: __f32}
   StructMember{[[ offset 8 ]] field1: __vec_2__f32}
   StructMember{[[ offset 16 ]] field2: __mat_2_2__f32}
@@ -621,7 +621,7 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule()) << p->error();
 
-  auto* type = p->ConvertType(3);
+  auto type = p->ConvertType(3);
   EXPECT_EQ(type, nullptr);
   EXPECT_THAT(p->error(),
               Eq("SPIR-V pointer type with ID 3 has invalid pointee type 42"));
@@ -644,11 +644,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kInput);
   EXPECT_TRUE(p->error().empty());
 }
@@ -660,11 +660,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kOutput);
   EXPECT_TRUE(p->error().empty());
 }
@@ -676,11 +676,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kUniform);
   EXPECT_TRUE(p->error().empty());
 }
@@ -692,11 +692,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kWorkgroup);
   EXPECT_TRUE(p->error().empty());
 }
@@ -708,11 +708,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kUniformConstant);
   EXPECT_TRUE(p->error().empty());
 }
@@ -724,11 +724,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kStorage);
   EXPECT_TRUE(p->error().empty());
 }
@@ -740,11 +740,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kImage);
   EXPECT_TRUE(p->error().empty());
 }
@@ -756,11 +756,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kPrivate);
   EXPECT_TRUE(p->error().empty());
 }
@@ -772,11 +772,11 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto type = p->ConvertType(3);
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kFunction);
   EXPECT_TRUE(p->error().empty());
 }
@@ -790,19 +790,19 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(3);
+  auto type = p->ConvertType(3);
   EXPECT_NE(type, nullptr);
-  EXPECT_TRUE(type->Is<sem::Pointer>());
+  EXPECT_TRUE(type.ast->Is<ast::Pointer>());
 
-  auto* ptr_ty = type->As<sem::Pointer>();
+  auto* ptr_ty = type.ast->As<ast::Pointer>();
   EXPECT_NE(ptr_ty, nullptr);
   EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kInput);
-  EXPECT_TRUE(ptr_ty->type()->Is<sem::Pointer>());
+  EXPECT_TRUE(ptr_ty->type()->Is<ast::Pointer>());
 
-  auto* ptr_ptr_ty = ptr_ty->type()->As<sem::Pointer>();
+  auto* ptr_ptr_ty = ptr_ty->type()->As<ast::Pointer>();
   EXPECT_NE(ptr_ptr_ty, nullptr);
   EXPECT_EQ(ptr_ptr_ty->storage_class(), ast::StorageClass::kOutput);
-  EXPECT_TRUE(ptr_ptr_ty->type()->Is<sem::F32>());
+  EXPECT_TRUE(ptr_ptr_ty->type()->Is<ast::F32>());
 
   EXPECT_TRUE(p->error().empty());
 }
@@ -814,8 +814,8 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(1);
-  EXPECT_TRUE(type->Is<sem::Void>());
+  auto type = p->ConvertType(1);
+  EXPECT_TRUE(type.ast->Is<ast::Void>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -827,8 +827,8 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(1);
-  EXPECT_TRUE(type->Is<sem::Void>());
+  auto type = p->ConvertType(1);
+  EXPECT_TRUE(type.ast->Is<ast::Void>());
   EXPECT_TRUE(p->error().empty());
 }
 
@@ -840,8 +840,8 @@
   )"));
   EXPECT_TRUE(p->BuildInternalModule());
 
-  auto* type = p->ConvertType(1);
-  EXPECT_TRUE(type->Is<sem::Void>());
+  auto type = p->ConvertType(1);
+  EXPECT_TRUE(type.ast->Is<ast::Void>());
   EXPECT_TRUE(p->error().empty());
 }
 
diff --git a/src/reader/spirv/parser_impl_test_helper.h b/src/reader/spirv/parser_impl_test_helper.h
index 240880a..56a7366 100644
--- a/src/reader/spirv/parser_impl_test_helper.h
+++ b/src/reader/spirv/parser_impl_test_helper.h
@@ -86,7 +86,7 @@
     return impl_.glsl_std_450_imports();
   }
 
-  sem::Type* ConvertType(uint32_t id) { return impl_.ConvertType(id); }
+  typ::Type ConvertType(uint32_t id) { return impl_.ConvertType(id); }
   DecorationList GetDecorationsFor(uint32_t id) const {
     return impl_.GetDecorationsFor(id);
   }
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index bd3e018..9304350 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -362,10 +362,12 @@
   /// @param msg the warning message
   void deprecated(const Source& source, const std::string& msg);
   /// Registers a constructed type into the parser
+  /// TODO(crbug.com/tint/724): Remove
   /// @param name the constructed name
   /// @param type the constructed type
   void register_constructed(const std::string& name, sem::Type* type);
   /// Retrieves a constructed type
+  /// TODO(crbug.com/tint/724): Remove
   /// @param name The name to lookup
   /// @returns the constructed type for `name` or `nullptr` if not found
   sem::Type* get_constructed(const std::string& name);
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index b97f189..ea5f413 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/ast/alias.h"
+#include "src/ast/array.h"
+#include "src/ast/matrix.h"
+#include "src/ast/sampler.h"
 #include "src/reader/wgsl/parser_impl_test_helper.h"
 #include "src/sem/sampled_texture_type.h"
 
@@ -43,14 +47,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
-  EXPECT_EQ(t.value, alias_type);
-  ASSERT_TRUE(t->Is<sem::Alias>());
-
-  auto* alias = t->As<sem::Alias>();
-  EXPECT_EQ(p->builder().Symbols().NameFor(alias->symbol()), "A");
-  EXPECT_EQ(alias->type(), int_type);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 2u}}));
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  auto* type_name = t.value.ast->As<ast::TypeName>();
+  ASSERT_NE(type_name, nullptr);
+  EXPECT_EQ(p->builder().Symbols().Get("A"), type_name->name());
+  EXPECT_EQ(type_name->source().range, (Source::Range{{1u, 1u}, {1u, 2u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Identifier_NotFound) {
@@ -73,9 +74,9 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   EXPECT_EQ(t.value, bool_type);
-  ASSERT_TRUE(t->Is<sem::Bool>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Bool>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
 }
 
@@ -88,9 +89,9 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   EXPECT_EQ(t.value, float_type);
-  ASSERT_TRUE(t->Is<sem::F32>());
+  ASSERT_TRUE(t.value.ast->Is<ast::F32>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
 }
 
@@ -103,24 +104,20 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   EXPECT_EQ(t.value, int_type);
-  ASSERT_TRUE(t->Is<sem::I32>());
+  ASSERT_TRUE(t.value.ast->Is<ast::I32>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_U32) {
   auto p = parser("u32");
 
-  auto& builder = p->builder();
-  auto* uint_type = builder.create<sem::U32>();
-
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
-  EXPECT_EQ(t.value, uint_type);
-  ASSERT_TRUE(t->Is<sem::U32>());
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_TRUE(t.value.ast->Is<ast::U32>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
 }
 
@@ -142,10 +139,10 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  EXPECT_TRUE(t->Is<sem::Vector>());
-  EXPECT_EQ(t->As<sem::Vector>()->size(), params.count);
+  EXPECT_TRUE(t.value.ast->Is<ast::Vector>());
+  EXPECT_EQ(t.value.ast->As<ast::Vector>()->size(), params.count);
   EXPECT_EQ(t.value.ast->source().range, params.range);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -232,12 +229,12 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::Pointer>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Pointer>());
 
-  auto* ptr = t->As<sem::Pointer>();
-  ASSERT_TRUE(ptr->type()->Is<sem::F32>());
+  auto* ptr = t.value.ast->As<ast::Pointer>();
+  ASSERT_TRUE(ptr->type()->Is<ast::F32>());
   ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction);
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
@@ -247,17 +244,17 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::Pointer>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Pointer>());
 
-  auto* ptr = t->As<sem::Pointer>();
-  ASSERT_TRUE(ptr->type()->Is<sem::Vector>());
+  auto* ptr = t.value.ast->As<ast::Pointer>();
+  ASSERT_TRUE(ptr->type()->Is<ast::Vector>());
   ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction);
 
-  auto* vec = ptr->type()->As<sem::Vector>();
+  auto* vec = ptr->type()->As<ast::Vector>();
   ASSERT_EQ(vec->size(), 2u);
-  ASSERT_TRUE(vec->type()->Is<sem::F32>());
+  ASSERT_TRUE(vec->type()->Is<ast::F32>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 25}}));
 }
 
@@ -346,14 +343,14 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_FALSE(a->IsRuntimeArray());
   ASSERT_EQ(a->size(), 5u);
-  ASSERT_TRUE(a->type()->Is<sem::F32>());
+  ASSERT_TRUE(a->type()->Is<ast::F32>());
   EXPECT_EQ(a->decorations().size(), 0u);
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 14u}}));
 }
@@ -363,14 +360,14 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_FALSE(a->IsRuntimeArray());
   ASSERT_EQ(a->size(), 5u);
-  ASSERT_TRUE(a->type()->Is<sem::F32>());
+  ASSERT_TRUE(a->type()->Is<ast::F32>());
 
   ASSERT_EQ(a->decorations().size(), 1u);
   auto* stride = a->decorations()[0];
@@ -384,13 +381,13 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
-  ASSERT_TRUE(a->type()->Is<sem::F32>());
+  ASSERT_TRUE(a->type()->Is<ast::F32>());
 
   ASSERT_EQ(a->decorations().size(), 1u);
   auto* stride = a->decorations()[0];
@@ -404,13 +401,13 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
-  ASSERT_TRUE(a->type()->Is<sem::F32>());
+  ASSERT_TRUE(a->type()->Is<ast::F32>());
 
   auto& decos = a->decorations();
   ASSERT_EQ(decos.size(), 2u);
@@ -426,13 +423,13 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
-  ASSERT_TRUE(a->type()->Is<sem::F32>());
+  ASSERT_TRUE(a->type()->Is<ast::F32>());
 
   auto& decos = a->decorations();
   ASSERT_EQ(decos.size(), 2u);
@@ -530,13 +527,13 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
-  ASSERT_TRUE(a->type()->Is<sem::U32>());
+  ASSERT_TRUE(a->type()->Is<ast::U32>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
 }
 
@@ -545,11 +542,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t->Is<sem::ArrayType>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
 
-  auto* a = t->As<sem::ArrayType>();
+  auto* a = t.value.ast->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
   ASSERT_TRUE(a->type()->is_unsigned_integer_vector());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
@@ -644,10 +641,10 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  EXPECT_TRUE(t->Is<sem::Matrix>());
-  auto* mat = t->As<sem::Matrix>();
+  EXPECT_TRUE(t.value.ast->Is<ast::Matrix>());
+  auto* mat = t.value.ast->As<ast::Matrix>();
   EXPECT_EQ(mat->rows(), params.rows);
   EXPECT_EQ(mat->columns(), params.columns);
   EXPECT_EQ(t.value.ast->source().range, params.range);
@@ -772,10 +769,10 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_NE(t.value.ast, nullptr) << p->error();
   EXPECT_EQ(t.value, type);
-  ASSERT_TRUE(t->Is<sem::Sampler>());
-  ASSERT_FALSE(t->As<sem::Sampler>()->IsComparison());
+  ASSERT_TRUE(t.value.ast->Is<ast::Sampler>());
+  ASSERT_FALSE(t.value.ast->As<ast::Sampler>()->IsComparison());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
 }
 
@@ -789,11 +786,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value, nullptr);
+  ASSERT_NE(t.value.ast, nullptr);
   EXPECT_EQ(t.value, type);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::SampledTexture>());
-  ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>());
+  ASSERT_TRUE(t.value.ast->Is<ast::Texture>());
+  ASSERT_TRUE(t.value.ast->Is<ast::SampledTexture>());
+  ASSERT_TRUE(t.value.ast->As<ast::SampledTexture>()->type()->Is<ast::F32>());
   EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 18u}}));
 }
 
diff --git a/src/writer/msl/generator_impl_binary_test.cc b/src/writer/msl/generator_impl_binary_test.cc
index 431c7e7..ae11182 100644
--- a/src/writer/msl/generator_impl_binary_test.cc
+++ b/src/writer/msl/generator_impl_binary_test.cc
@@ -31,11 +31,11 @@
 TEST_P(MslBinaryTest, Emit) {
   auto params = GetParam();
 
-  auto type = [&]() {
+  auto type = [&] {
     return ((params.op == ast::BinaryOp::kLogicalAnd) ||
             (params.op == ast::BinaryOp::kLogicalOr))
-               ? static_cast<typ::Type>(ty.bool_())
-               : static_cast<typ::Type>(ty.u32());
+               ? static_cast<ast::Type*>(ty.bool_())
+               : static_cast<ast::Type*>(ty.u32());
   };
 
   auto* left = Var("left", type(), ast::StorageClass::kFunction);