reader/wgsl: Migrate to AST types

Bug: tint:724
Change-Id: I484813dd139122244cd09829ab5b035cec9981e6
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49960
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 9cfe32e..90e4c8b 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -213,7 +213,7 @@
 
 ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default;
 
-ParserImpl::TypedIdentifier::TypedIdentifier(typ::Type type_in,
+ParserImpl::TypedIdentifier::TypedIdentifier(ast::Type* type_in,
                                              std::string name_in,
                                              Source source_in)
     : type(type_in), name(std::move(name_in)), source(std::move(source_in)) {}
@@ -227,7 +227,7 @@
 ParserImpl::FunctionHeader::FunctionHeader(Source src,
                                            std::string n,
                                            ast::VariableList p,
-                                           typ::Type ret_ty,
+                                           ast::Type* ret_ty,
                                            ast::DecorationList ret_decos)
     : source(src),
       name(n),
@@ -247,7 +247,7 @@
 ParserImpl::VarDeclInfo::VarDeclInfo(Source source_in,
                                      std::string name_in,
                                      ast::StorageClass storage_class_in,
-                                     typ::Type type_in)
+                                     ast::Type* type_in)
     : source(std::move(source_in)),
       name(std::move(name_in)),
       storage_class(storage_class_in),
@@ -317,11 +317,11 @@
 }
 
 void ParserImpl::register_constructed(const std::string& name,
-                                      sem::Type* type) {
+                                      const ast::Type* type) {
   registered_constructs_[name] = type;
 }
 
-sem::Type* ParserImpl::get_constructed(const std::string& name) {
+const ast::Type* ParserImpl::get_constructed(const std::string& name) {
   if (registered_constructs_.find(name) == registered_constructs_.end()) {
     return nullptr;
   }
@@ -401,7 +401,7 @@
       if (!expect("type alias", Token::Type::kSemicolon))
         return Failure::kErrored;
 
-      builder_.AST().AddConstructedType(const_cast<ast::Alias*>(ta.value.ast));
+      builder_.AST().AddConstructedType(const_cast<ast::Alias*>(ta.value));
       return true;
     }
 
@@ -413,10 +413,9 @@
       if (!expect("struct declaration", Token::Type::kSemicolon))
         return Failure::kErrored;
 
-      register_constructed(
-          builder_.Symbols().NameFor(str.value->impl()->name()), str.value);
-      builder_.AST().AddConstructedType(
-          const_cast<ast::Struct*>(str.value.ast));
+      register_constructed(builder_.Symbols().NameFor(str.value->name()),
+                           str.value);
+      builder_.AST().AddConstructedType(str.value);
       return true;
     }
 
@@ -565,8 +564,7 @@
   if (decl.errored)
     return Failure::kErrored;
 
-  if ((decl->type.sem && decl->type.sem->UnwrapAll()->is_handle()) ||
-      (decl->type.ast && decl->type.ast->UnwrapAll()->is_handle())) {
+  if (decl->type && decl->type->UnwrapAll()->is_handle()) {
     // handle types implicitly have the `UniformConstant` storage class.
     if (explicit_sc.matched) {
       return add_error(
@@ -586,7 +584,7 @@
 //  | sampled_texture_type LESS_THAN type_decl GREATER_THAN
 //  | multisampled_texture_type LESS_THAN type_decl GREATER_THAN
 //  | storage_texture_type LESS_THAN image_storage_type GREATER_THAN
-Maybe<typ::Type> ParserImpl::texture_sampler_types() {
+Maybe<ast::Type*> ParserImpl::texture_sampler_types() {
   auto type = sampler_type();
   if (type.matched)
     return type;
@@ -644,7 +642,7 @@
 // sampler_type
 //  : SAMPLER
 //  | SAMPLER_COMPARISON
-Maybe<typ::Type> ParserImpl::sampler_type() {
+Maybe<ast::Type*> ParserImpl::sampler_type() {
   Source source;
   if (match(Token::Type::kSampler, &source))
     return builder_.ty.sampler(source, ast::SamplerKind::kSampler);
@@ -686,7 +684,7 @@
 
 // external_texture_type
 //  : TEXTURE_EXTERNAL
-Maybe<typ::Type> ParserImpl::external_texture_type() {
+Maybe<ast::Type*> ParserImpl::external_texture_type() {
   Source source;
   if (match(Token::Type::kTextureExternal, &source)) {
     return builder_.ty.external_texture(source);
@@ -727,7 +725,7 @@
 //  | TEXTURE_DEPTH_2D_ARRAY
 //  | TEXTURE_DEPTH_CUBE
 //  | TEXTURE_DEPTH_CUBE_ARRAY
-Maybe<typ::Type> ParserImpl::depth_texture_type() {
+Maybe<ast::Type*> ParserImpl::depth_texture_type() {
   Source source;
 
   if (match(Token::Type::kTextureDepth2d, &source))
@@ -921,7 +919,7 @@
   if (access_decos.size() > 1)
     return add_error(ident.source, "multiple access decorations not allowed");
 
-  typ::Type ty = type.value;
+  ast::Type* ty = type.value;
 
   for (auto* deco : access_decos) {
     // If we have an access control decoration then we take it and wrap our
@@ -965,7 +963,7 @@
 
 // type_alias
 //   : TYPE IDENT EQUAL type_decl
-Maybe<typ::Alias> ParserImpl::type_alias() {
+Maybe<ast::Alias*> ParserImpl::type_alias() {
   auto t = peek();
   if (!t.IsType())
     return Failure::kNoMatch;
@@ -1017,7 +1015,7 @@
 //   | MAT4x3 LESS_THAN type_decl GREATER_THAN
 //   | MAT4x4 LESS_THAN type_decl GREATER_THAN
 //   | texture_sampler_types
-Maybe<typ::Type> ParserImpl::type_decl() {
+Maybe<ast::Type*> ParserImpl::type_decl() {
   auto decos = decoration_list();
   if (decos.errored)
     return Failure::kErrored;
@@ -1034,7 +1032,7 @@
   return type;
 }
 
-Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) {
+Maybe<ast::Type*> ParserImpl::type_decl(ast::DecorationList& decos) {
   auto t = peek();
   Source source;
   if (match(Token::Type::kIdentifier, &source)) {
@@ -1043,9 +1041,8 @@
     if (ty == nullptr)
       return add_error(t, "unknown constructed type '" + t.to_str() + "'");
 
-    return typ::Type{builder_.create<ast::TypeName>(
-                         source, builder_.Symbols().Register(t.to_str())),
-                     ty};
+    return builder_.create<ast::TypeName>(
+        source, builder_.Symbols().Register(t.to_str()));
   }
 
   if (match(Token::Type::kBool, &source))
@@ -1088,7 +1085,7 @@
   return Failure::kNoMatch;
 }
 
-Expect<typ::Type> ParserImpl::expect_type(const std::string& use) {
+Expect<ast::Type*> ParserImpl::expect_type(const std::string& use) {
   auto type = type_decl();
   if (type.errored)
     return Failure::kErrored;
@@ -1097,12 +1094,12 @@
   return type.value;
 }
 
-Expect<typ::Type> ParserImpl::expect_type_decl_pointer(Token t) {
+Expect<ast::Type*> ParserImpl::expect_type_decl_pointer(Token t) {
   const char* use = "ptr declaration";
 
   ast::StorageClass storage_class = ast::StorageClass::kNone;
 
-  auto subtype = expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
+  auto subtype = expect_lt_gt_block(use, [&]() -> Expect<ast::Type*> {
     auto sc = expect_storage_class(use);
     if (sc.errored)
       return Failure::kErrored;
@@ -1126,7 +1123,7 @@
                              storage_class);
 }
 
-Expect<typ::Type> ParserImpl::expect_type_decl_vector(Token t) {
+Expect<ast::Type*> ParserImpl::expect_type_decl_vector(Token t) {
   uint32_t count = 2;
   if (t.IsVec3())
     count = 3;
@@ -1143,14 +1140,14 @@
                          count);
 }
 
-Expect<typ::Type> ParserImpl::expect_type_decl_array(
+Expect<ast::Type*> ParserImpl::expect_type_decl_array(
     Token t,
     ast::DecorationList decos) {
   const char* use = "array declaration";
 
   uint32_t size = 0;
 
-  auto subtype = expect_lt_gt_block(use, [&]() -> Expect<typ::Type> {
+  auto subtype = expect_lt_gt_block(use, [&]() -> Expect<ast::Type*> {
     auto type = expect_type(use);
     if (type.errored)
       return Failure::kErrored;
@@ -1173,7 +1170,7 @@
                            size, std::move(decos));
 }
 
-Expect<typ::Type> ParserImpl::expect_type_decl_matrix(Token t) {
+Expect<ast::Type*> ParserImpl::expect_type_decl_matrix(Token t) {
   uint32_t rows = 2;
   uint32_t columns = 2;
   if (t.IsMat3x2() || t.IsMat3x3() || t.IsMat3x4()) {
@@ -1239,7 +1236,7 @@
 
 // struct_decl
 //   : struct_decoration_decl* STRUCT IDENT struct_body_decl
-Maybe<typ::Struct> ParserImpl::struct_decl(ast::DecorationList& decos) {
+Maybe<ast::Struct*> ParserImpl::struct_decl(ast::DecorationList& decos) {
   auto t = peek();
   auto source = t.source();
 
@@ -1255,9 +1252,8 @@
     return Failure::kErrored;
 
   auto sym = builder_.Symbols().Register(name.value);
-  auto* str =
-      create<ast::Struct>(source, sym, std::move(body.value), std::move(decos));
-  return typ::Struct{str, create<sem::StructType>(str)};
+  return create<ast::Struct>(source, sym, std::move(body.value),
+                             std::move(decos));
 }
 
 // struct_body_decl
@@ -1346,7 +1342,7 @@
 // function_type_decl
 //   : type_decl
 //   | VOID
-Maybe<typ::Type> ParserImpl::function_type_decl() {
+Maybe<ast::Type*> ParserImpl::function_type_decl() {
   Source source;
   if (match(Token::Type::kVoid, &source))
     return builder_.ty.void_(source);
@@ -1381,7 +1377,7 @@
     }
   }
 
-  typ::Type return_type;
+  ast::Type* return_type = nullptr;
   ast::DecorationList return_decorations;
 
   if (match(Token::Type::kArrow)) {
@@ -1402,7 +1398,7 @@
       return_type = type.value;
     }
 
-    if (Is<ast::Void>(return_type.ast)) {
+    if (Is<ast::Void>(return_type)) {
       // crbug.com/tint/677: void has been removed from the language
       deprecated(tok.source(),
                  "omit '-> void' for functions that do not return a value");
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 9304350..513da69 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -211,12 +211,12 @@
     /// @param type_in parsed type
     /// @param name_in parsed identifier
     /// @param source_in source to the identifier
-    TypedIdentifier(typ::Type type_in, std::string name_in, Source source_in);
+    TypedIdentifier(ast::Type* type_in, std::string name_in, Source source_in);
     /// Destructor
     ~TypedIdentifier();
 
     /// Parsed type.
-    typ::Type type;
+    ast::Type* type;
     /// Parsed identifier.
     std::string name;
     /// Source to the identifier.
@@ -239,7 +239,7 @@
     FunctionHeader(Source src,
                    std::string n,
                    ast::VariableList p,
-                   typ::Type ret_ty,
+                   ast::Type* ret_ty,
                    ast::DecorationList ret_decos);
     /// Destructor
     ~FunctionHeader();
@@ -255,7 +255,7 @@
     /// Function parameters
     ast::VariableList params;
     /// Function return type
-    typ::Type return_type;
+    ast::Type* return_type;
     /// Function return type decorations
     ast::DecorationList return_type_decorations;
   };
@@ -275,7 +275,7 @@
     VarDeclInfo(Source source_in,
                 std::string name_in,
                 ast::StorageClass storage_class_in,
-                typ::Type type_in);
+                ast::Type* type_in);
     /// Destructor
     ~VarDeclInfo();
 
@@ -286,7 +286,7 @@
     /// Variable storage class
     ast::StorageClass storage_class;
     /// Variable type
-    typ::Type type;
+    ast::Type* type = nullptr;
   };
 
   /// Creates a new parser using the given file
@@ -365,12 +365,12 @@
   /// 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);
+  void register_constructed(const std::string& name, const ast::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);
+  const ast::Type* get_constructed(const std::string& name);
 
   /// Parses the `translation_unit` grammar element
   void translation_unit();
@@ -400,15 +400,15 @@
   Maybe<ast::StorageClass> variable_storage_decoration();
   /// Parses a `type_alias` grammar element
   /// @returns the type alias or nullptr on error
-  Maybe<typ::Alias> type_alias();
+  Maybe<ast::Alias*> type_alias();
   /// Parses a `type_decl` grammar element
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<typ::Type> type_decl();
+  Maybe<ast::Type*> type_decl();
   /// Parses a `type_decl` grammar element with the given pre-parsed
   /// decorations.
   /// @param decos the list of decorations for the type.
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<typ::Type> type_decl(ast::DecorationList& decos);
+  Maybe<ast::Type*> type_decl(ast::DecorationList& decos);
   /// Parses a `storage_class` grammar element, erroring on parse failure.
   /// @param use a description of what was being parsed if an error was raised.
   /// @returns the storage class or StorageClass::kNone if none matched
@@ -417,7 +417,7 @@
   /// `struct_decoration_decl*` provided as `decos`.
   /// @returns the struct type or nullptr on error
   /// @param decos the list of decorations for the struct declaration.
-  Maybe<typ::Struct> struct_decl(ast::DecorationList& decos);
+  Maybe<ast::Struct*> struct_decl(ast::DecorationList& decos);
   /// Parses a `struct_body_decl` grammar element, erroring on parse failure.
   /// @returns the struct members
   Expect<ast::StructMemberList> expect_struct_body_decl();
@@ -434,10 +434,10 @@
   Maybe<ast::Function*> function_decl(ast::DecorationList& decos);
   /// Parses a `texture_sampler_types` grammar element
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<typ::Type> texture_sampler_types();
+  Maybe<ast::Type*> texture_sampler_types();
   /// Parses a `sampler_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<typ::Type> sampler_type();
+  Maybe<ast::Type*> sampler_type();
   /// Parses a `multisampled_texture_type` grammar element
   /// @returns returns the multisample texture dimension or kNone if none
   /// matched.
@@ -451,17 +451,17 @@
   Maybe<ast::TextureDimension> storage_texture_type();
   /// Parses a `depth_texture_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<typ::Type> depth_texture_type();
+  Maybe<ast::Type*> depth_texture_type();
   /// Parses a 'texture_external_type' grammar element
   /// @returns the parsed Type or nullptr if none matched
-  Maybe<typ::Type> external_texture_type();
+  Maybe<ast::Type*> external_texture_type();
   /// Parses a `image_storage_type` grammar element
   /// @param use a description of what was being parsed if an error was raised
   /// @returns returns the image format or kNone if none matched.
   Expect<ast::ImageFormat> expect_image_storage_type(const std::string& use);
   /// Parses a `function_type_decl` grammar element
   /// @returns the parsed type or nullptr otherwise
-  Maybe<typ::Type> function_type_decl();
+  Maybe<ast::Type*> function_type_decl();
   /// Parses a `function_header` grammar element
   /// @returns the parsed function header
   Maybe<FunctionHeader> function_header();
@@ -827,12 +827,12 @@
   /// Used to ensure that all decorations are consumed.
   bool expect_decorations_consumed(const ast::DecorationList& list);
 
-  Expect<typ::Type> expect_type_decl_pointer(Token t);
-  Expect<typ::Type> expect_type_decl_vector(Token t);
-  Expect<typ::Type> expect_type_decl_array(Token t, ast::DecorationList decos);
-  Expect<typ::Type> expect_type_decl_matrix(Token t);
+  Expect<ast::Type*> expect_type_decl_pointer(Token t);
+  Expect<ast::Type*> expect_type_decl_vector(Token t);
+  Expect<ast::Type*> expect_type_decl_array(Token t, ast::DecorationList decos);
+  Expect<ast::Type*> expect_type_decl_matrix(Token t);
 
-  Expect<typ::Type> expect_type(const std::string& use);
+  Expect<ast::Type*> expect_type(const std::string& use);
 
   Maybe<ast::Statement*> non_block_statement();
   Maybe<ast::Statement*> for_header_initializer();
@@ -858,7 +858,7 @@
   uint32_t sync_depth_ = 0;
   std::vector<Token::Type> sync_tokens_;
   int silence_errors_ = 0;
-  std::unordered_map<std::string, sem::Type*> registered_constructs_;
+  std::unordered_map<std::string, const ast::Type*> registered_constructs_;
   ProgramBuilder builder_;
   size_t max_errors_ = 25;
 };
diff --git a/src/reader/wgsl/parser_impl_depth_texture_type_test.cc b/src/reader/wgsl/parser_impl_depth_texture_type_test.cc
index 82b73c1..e41b2e9 100644
--- a/src/reader/wgsl/parser_impl_depth_texture_type_test.cc
+++ b/src/reader/wgsl/parser_impl_depth_texture_type_test.cc
@@ -34,11 +34,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::DepthTexture>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::DepthTexture>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
   EXPECT_FALSE(p->has_error());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
 }
 
 TEST_F(ParserImplTest, DepthTextureType_2dArray) {
@@ -47,11 +47,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::DepthTexture>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2dArray);
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::DepthTexture>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2dArray);
   EXPECT_FALSE(p->has_error());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 23u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 23u}}));
 }
 
 TEST_F(ParserImplTest, DepthTextureType_Cube) {
@@ -60,11 +60,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::DepthTexture>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCube);
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::DepthTexture>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::kCube);
   EXPECT_FALSE(p->has_error());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
 
 TEST_F(ParserImplTest, DepthTextureType_CubeArray) {
@@ -73,11 +73,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::DepthTexture>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::kCubeArray);
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::DepthTexture>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::kCubeArray);
   EXPECT_FALSE(p->has_error());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 25u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 25u}}));
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_external_texture_type_test.cc b/src/reader/wgsl/parser_impl_external_texture_type_test.cc
index 9f46112..35f4c40 100644
--- a/src/reader/wgsl/parser_impl_external_texture_type_test.cc
+++ b/src/reader/wgsl/parser_impl_external_texture_type_test.cc
@@ -32,7 +32,7 @@
   auto t = p->external_texture_type();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_function_header_test.cc b/src/reader/wgsl/parser_impl_function_header_test.cc
index f509264..52071a3 100644
--- a/src/reader/wgsl/parser_impl_function_header_test.cc
+++ b/src/reader/wgsl/parser_impl_function_header_test.cc
@@ -30,7 +30,7 @@
   ASSERT_EQ(f->params.size(), 2u);
   EXPECT_EQ(f->params[0]->symbol(), p->builder().Symbols().Get("a"));
   EXPECT_EQ(f->params[1]->symbol(), p->builder().Symbols().Get("b"));
-  EXPECT_TRUE(f->return_type->Is<sem::Void>());
+  EXPECT_TRUE(f->return_type->Is<ast::Void>());
 }
 
 TEST_F(ParserImplTest, FunctionHeader_TrailingComma) {
@@ -42,7 +42,7 @@
   EXPECT_EQ(f->name, "main");
   ASSERT_EQ(f->params.size(), 1u);
   EXPECT_EQ(f->params[0]->symbol(), p->builder().Symbols().Get("a"));
-  EXPECT_TRUE(f->return_type->Is<sem::Void>());
+  EXPECT_TRUE(f->return_type->Is<ast::Void>());
 }
 
 TEST_F(ParserImplTest, FunctionHeader_DecoratedReturnType) {
@@ -54,7 +54,7 @@
 
   EXPECT_EQ(f->name, "main");
   EXPECT_EQ(f->params.size(), 0u);
-  EXPECT_TRUE(f->return_type->Is<sem::F32>());
+  EXPECT_TRUE(f->return_type->Is<ast::F32>());
   ASSERT_EQ(f->return_type_decorations.size(), 1u);
   auto* loc = f->return_type_decorations[0]->As<ast::LocationDecoration>();
   ASSERT_TRUE(loc != nullptr);
diff --git a/src/reader/wgsl/parser_impl_function_type_decl_test.cc b/src/reader/wgsl/parser_impl_function_type_decl_test.cc
index ed4eb5a..c9865ab 100644
--- a/src/reader/wgsl/parser_impl_function_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_function_type_decl_test.cc
@@ -22,28 +22,25 @@
 TEST_F(ParserImplTest, FunctionTypeDecl_Void) {
   auto p = parser("void");
 
-  auto* v = p->builder().create<sem::Void>();
-
   auto e = p->function_type_decl();
   EXPECT_TRUE(e.matched);
   EXPECT_FALSE(e.errored);
   EXPECT_FALSE(p->has_error()) << p->error();
-  ASSERT_EQ(e.value, v);
-  EXPECT_EQ(e.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
+  EXPECT_TRUE(e.value->Is<ast::Void>());
+  EXPECT_EQ(e.value->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
 }
 
 TEST_F(ParserImplTest, FunctionTypeDecl_Type) {
   auto p = parser("vec2<f32>");
 
-  auto* f32 = p->builder().create<sem::F32>();
-  auto* vec2 = p->builder().create<sem::Vector>(f32, 2);
-
   auto e = p->function_type_decl();
   EXPECT_TRUE(e.matched);
   EXPECT_FALSE(e.errored);
   EXPECT_FALSE(p->has_error()) << p->error();
-  ASSERT_EQ(e.value, vec2);
-  EXPECT_EQ(e.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 10u}}));
+  ASSERT_TRUE(e.value->Is<ast::Vector>());
+  EXPECT_EQ(e.value->As<ast::Vector>()->size(), 2u);
+  EXPECT_TRUE(e.value->As<ast::Vector>()->type()->Is<ast::F32>());
+  EXPECT_EQ(e.value->source().range, (Source::Range{{1u, 1u}, {1u, 10u}}));
 }
 
 TEST_F(ParserImplTest, FunctionTypeDecl_InvalidType) {
diff --git a/src/reader/wgsl/parser_impl_sampler_type_test.cc b/src/reader/wgsl/parser_impl_sampler_type_test.cc
index 9ac9f6e..a7891e2 100644
--- a/src/reader/wgsl/parser_impl_sampler_type_test.cc
+++ b/src/reader/wgsl/parser_impl_sampler_type_test.cc
@@ -34,10 +34,10 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Sampler>());
-  EXPECT_FALSE(t->As<sem::Sampler>()->IsComparison());
+  ASSERT_TRUE(t->Is<ast::Sampler>());
+  EXPECT_FALSE(t->As<ast::Sampler>()->IsComparison());
   EXPECT_FALSE(p->has_error());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
 }
 
 TEST_F(ParserImplTest, SamplerType_ComparisonSampler) {
@@ -46,10 +46,10 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Sampler>());
-  EXPECT_TRUE(t->As<sem::Sampler>()->IsComparison());
+  ASSERT_TRUE(t->Is<ast::Sampler>());
+  EXPECT_TRUE(t->As<ast::Sampler>()->IsComparison());
   EXPECT_FALSE(p->has_error());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_struct_decl_test.cc b/src/reader/wgsl/parser_impl_struct_decl_test.cc
index fad7874..d98fb5c 100644
--- a/src/reader/wgsl/parser_impl_struct_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_struct_decl_test.cc
@@ -36,12 +36,10 @@
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
   ASSERT_NE(s.value, nullptr);
-  ASSERT_EQ(s->impl()->name(), p->builder().Symbols().Register("S"));
-  ASSERT_EQ(s->impl()->members().size(), 2u);
-  EXPECT_EQ(s->impl()->members()[0]->symbol(),
-            p->builder().Symbols().Register("a"));
-  EXPECT_EQ(s->impl()->members()[1]->symbol(),
-            p->builder().Symbols().Register("b"));
+  ASSERT_EQ(s->name(), p->builder().Symbols().Register("S"));
+  ASSERT_EQ(s->members().size(), 2u);
+  EXPECT_EQ(s->members()[0]->symbol(), p->builder().Symbols().Register("a"));
+  EXPECT_EQ(s->members()[1]->symbol(), p->builder().Symbols().Register("b"));
 }
 
 TEST_F(ParserImplTest, StructDecl_ParsesWithDecoration) {
@@ -60,14 +58,12 @@
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
   ASSERT_NE(s.value, nullptr);
-  ASSERT_EQ(s->impl()->name(), p->builder().Symbols().Register("B"));
-  ASSERT_EQ(s->impl()->members().size(), 2u);
-  EXPECT_EQ(s->impl()->members()[0]->symbol(),
-            p->builder().Symbols().Register("a"));
-  EXPECT_EQ(s->impl()->members()[1]->symbol(),
-            p->builder().Symbols().Register("b"));
-  ASSERT_EQ(s->impl()->decorations().size(), 1u);
-  EXPECT_TRUE(s->impl()->decorations()[0]->Is<ast::StructBlockDecoration>());
+  ASSERT_EQ(s->name(), p->builder().Symbols().Register("B"));
+  ASSERT_EQ(s->members().size(), 2u);
+  EXPECT_EQ(s->members()[0]->symbol(), p->builder().Symbols().Register("a"));
+  EXPECT_EQ(s->members()[1]->symbol(), p->builder().Symbols().Register("b"));
+  ASSERT_EQ(s->decorations().size(), 1u);
+  EXPECT_TRUE(s->decorations()[0]->Is<ast::StructBlockDecoration>());
 }
 
 TEST_F(ParserImplTest, StructDecl_ParsesWithMultipleDecoration) {
@@ -87,15 +83,13 @@
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
   ASSERT_NE(s.value, nullptr);
-  ASSERT_EQ(s->impl()->name(), p->builder().Symbols().Register("S"));
-  ASSERT_EQ(s->impl()->members().size(), 2u);
-  EXPECT_EQ(s->impl()->members()[0]->symbol(),
-            p->builder().Symbols().Register("a"));
-  EXPECT_EQ(s->impl()->members()[1]->symbol(),
-            p->builder().Symbols().Register("b"));
-  ASSERT_EQ(s->impl()->decorations().size(), 2u);
-  EXPECT_TRUE(s->impl()->decorations()[0]->Is<ast::StructBlockDecoration>());
-  EXPECT_TRUE(s->impl()->decorations()[1]->Is<ast::StructBlockDecoration>());
+  ASSERT_EQ(s->name(), p->builder().Symbols().Register("S"));
+  ASSERT_EQ(s->members().size(), 2u);
+  EXPECT_EQ(s->members()[0]->symbol(), p->builder().Symbols().Register("a"));
+  EXPECT_EQ(s->members()[1]->symbol(), p->builder().Symbols().Register("b"));
+  ASSERT_EQ(s->decorations().size(), 2u);
+  EXPECT_TRUE(s->decorations()[0]->Is<ast::StructBlockDecoration>());
+  EXPECT_TRUE(s->decorations()[1]->Is<ast::StructBlockDecoration>());
 }
 
 TEST_F(ParserImplTest, StructDecl_EmptyMembers) {
@@ -110,7 +104,7 @@
   EXPECT_FALSE(s.errored);
   EXPECT_TRUE(s.matched);
   ASSERT_NE(s.value, nullptr);
-  ASSERT_EQ(s->impl()->members().size(), 0u);
+  ASSERT_EQ(s->members().size(), 0u);
 }
 
 TEST_F(ParserImplTest, StructDecl_MissingIdent) {
diff --git a/src/reader/wgsl/parser_impl_test.cc b/src/reader/wgsl/parser_impl_test.cc
index 861b819..3c415f6 100644
--- a/src/reader/wgsl/parser_impl_test.cc
+++ b/src/reader/wgsl/parser_impl_test.cc
@@ -57,7 +57,7 @@
 
   auto* alias = p->get_constructed("my_alias");
   ASSERT_NE(alias, nullptr);
-  ASSERT_EQ(alias, ty.i32());
+  EXPECT_TRUE(alias->Is<ast::I32>());
 }
 
 TEST_F(ParserImplTest, GetUnregisteredType) {
diff --git a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
index 646100d..2406ae7 100644
--- a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
+++ b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
@@ -38,9 +38,9 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Sampler>());
-  ASSERT_FALSE(t->As<sem::Sampler>()->IsComparison());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
+  ASSERT_TRUE(t->Is<ast::Sampler>());
+  ASSERT_FALSE(t->As<ast::Sampler>()->IsComparison());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
@@ -50,9 +50,9 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Sampler>());
-  ASSERT_TRUE(t->As<sem::Sampler>()->IsComparison());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
+  ASSERT_TRUE(t->Is<ast::Sampler>());
+  ASSERT_TRUE(t->As<ast::Sampler>()->IsComparison());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
@@ -62,10 +62,10 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::DepthTexture>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::DepthTexture>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
@@ -75,11 +75,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::SampledTexture>());
-  ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::F32>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k1d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::SampledTexture>());
+  ASSERT_TRUE(t->As<ast::SampledTexture>()->type()->Is<ast::F32>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k1d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
@@ -89,11 +89,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::SampledTexture>());
-  ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::I32>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::SampledTexture>());
+  ASSERT_TRUE(t->As<ast::SampledTexture>()->type()->Is<ast::I32>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
@@ -103,11 +103,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::SampledTexture>());
-  ASSERT_TRUE(t->As<sem::SampledTexture>()->type()->Is<sem::U32>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k3d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::SampledTexture>());
+  ASSERT_TRUE(t->As<ast::SampledTexture>()->type()->Is<ast::U32>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k3d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 16u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_Invalid) {
@@ -157,11 +157,11 @@
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::MultisampledTexture>());
-  ASSERT_TRUE(t->As<sem::MultisampledTexture>()->type()->Is<sem::I32>());
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::MultisampledTexture>());
+  ASSERT_TRUE(t->As<ast::MultisampledTexture>()->type()->Is<ast::I32>());
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_Invalid) {
@@ -212,12 +212,12 @@
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
 
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::StorageTexture>());
-  EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(),
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::StorageTexture>());
+  EXPECT_EQ(t->As<ast::StorageTexture>()->image_format(),
             ast::ImageFormat::kR8Unorm);
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k1d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 28u}}));
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k1d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 28u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
@@ -228,12 +228,12 @@
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
 
-  ASSERT_TRUE(t->Is<sem::Texture>());
-  ASSERT_TRUE(t->Is<sem::StorageTexture>());
-  EXPECT_EQ(t->As<sem::StorageTexture>()->image_format(),
+  ASSERT_TRUE(t->Is<ast::Texture>());
+  ASSERT_TRUE(t->Is<ast::StorageTexture>());
+  EXPECT_EQ(t->As<ast::StorageTexture>()->image_format(),
             ast::ImageFormat::kR16Float);
-  EXPECT_EQ(t->As<sem::Texture>()->dim(), ast::TextureDimension::k2d);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
+  EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType) {
diff --git a/src/reader/wgsl/parser_impl_type_alias_test.cc b/src/reader/wgsl/parser_impl_type_alias_test.cc
index d7f7203..fba0e5c 100644
--- a/src/reader/wgsl/parser_impl_type_alias_test.cc
+++ b/src/reader/wgsl/parser_impl_type_alias_test.cc
@@ -22,19 +22,16 @@
 TEST_F(ParserImplTest, TypeDecl_ParsesType) {
   auto p = parser("type a = i32");
 
-  auto* i32 = p->builder().create<sem::I32>();
-
   auto t = p->type_alias();
   EXPECT_FALSE(p->has_error());
   EXPECT_FALSE(t.errored);
   EXPECT_TRUE(t.matched);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Alias>());
-  auto* alias = t->As<sem::Alias>();
-  ASSERT_TRUE(alias->type()->Is<sem::I32>());
-  ASSERT_EQ(alias->type(), i32);
+  ASSERT_TRUE(t->Is<ast::Alias>());
+  auto* alias = t->As<ast::Alias>();
+  ASSERT_TRUE(alias->type()->Is<ast::I32>());
 
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 13u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 13u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_ParsesStruct_Ident) {
@@ -48,16 +45,11 @@
   EXPECT_FALSE(t.errored);
   EXPECT_TRUE(t.matched);
   ASSERT_NE(t.value, nullptr);
-  ASSERT_TRUE(t->Is<sem::Alias>());
-  auto* alias = t->As<sem::Alias>();
+  ASSERT_TRUE(t.value->Is<ast::Alias>());
+  auto* alias = t.value->As<ast::Alias>();
   EXPECT_EQ(p->builder().Symbols().NameFor(alias->symbol()), "a");
-  ASSERT_TRUE(alias->type()->Is<sem::StructType>());
-
-  auto* s = alias->type()->As<sem::StructType>();
-  EXPECT_EQ(s->impl()->name(), p->builder().Symbols().Get("B"));
-  EXPECT_EQ(s->impl()->name(), p->builder().Symbols().Get("B"));
-
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
+  EXPECT_TRUE(alias->type()->Is<ast::TypeName>());
+  EXPECT_EQ(alias->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_MissingIdent) {
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index ea5f413..af10a76 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -37,18 +37,14 @@
   auto p = parser("A");
 
   auto& builder = p->builder();
-
-  auto* int_type = builder.create<sem::I32>();
-  auto* alias_type =
-      builder.create<sem::Alias>(builder.Symbols().Register("A"), int_type);
-
+  auto alias_type = builder.ty.alias("A", builder.ty.i32());
   p->register_constructed("A", alias_type);
 
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
-  auto* type_name = t.value.ast->As<ast::TypeName>();
+  ASSERT_NE(t.value, nullptr) << p->error();
+  auto* type_name = t.value->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}}));
@@ -68,46 +64,34 @@
 TEST_F(ParserImplTest, TypeDecl_Bool) {
   auto p = parser("bool");
 
-  auto& builder = p->builder();
-  auto* bool_type = builder.create<sem::Bool>();
-
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
-  EXPECT_EQ(t.value, bool_type);
-  ASSERT_TRUE(t.value.ast->Is<ast::Bool>());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
+  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_TRUE(t.value->Is<ast::Bool>());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 5u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_F32) {
   auto p = parser("f32");
 
-  auto& builder = p->builder();
-  auto* float_type = builder.create<sem::F32>();
-
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
-  EXPECT_EQ(t.value, float_type);
-  ASSERT_TRUE(t.value.ast->Is<ast::F32>());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_TRUE(t.value->Is<ast::F32>());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_I32) {
   auto p = parser("i32");
 
-  auto& builder = p->builder();
-  auto* int_type = builder.create<sem::I32>();
-
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
-  EXPECT_EQ(t.value, int_type);
-  ASSERT_TRUE(t.value.ast->Is<ast::I32>());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_TRUE(t.value->Is<ast::I32>());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_U32) {
@@ -116,9 +100,9 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  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}}));
+  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_TRUE(t.value->Is<ast::U32>());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 4u}}));
 }
 
 struct VecData {
@@ -139,11 +123,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  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);
+  EXPECT_TRUE(t.value->Is<ast::Vector>());
+  EXPECT_EQ(t.value->As<ast::Vector>()->size(), params.count);
+  EXPECT_EQ(t.value->source().range, params.range);
 }
 INSTANTIATE_TEST_SUITE_P(
     ParserImplTest,
@@ -229,14 +213,14 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Pointer>());
+  ASSERT_TRUE(t.value->Is<ast::Pointer>());
 
-  auto* ptr = t.value.ast->As<ast::Pointer>();
+  auto* ptr = t.value->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}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
@@ -244,18 +228,18 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Pointer>());
+  ASSERT_TRUE(t.value->Is<ast::Pointer>());
 
-  auto* ptr = t.value.ast->As<ast::Pointer>();
+  auto* ptr = t.value->As<ast::Pointer>();
   ASSERT_TRUE(ptr->type()->Is<ast::Vector>());
   ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction);
 
   auto* vec = ptr->type()->As<ast::Vector>();
   ASSERT_EQ(vec->size(), 2u);
   ASSERT_TRUE(vec->type()->Is<ast::F32>());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 25}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 25}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingLessThan) {
@@ -343,16 +327,16 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->As<ast::Array>();
   ASSERT_FALSE(a->IsRuntimeArray());
   ASSERT_EQ(a->size(), 5u);
   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}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 14u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
@@ -360,11 +344,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->As<ast::Array>();
   ASSERT_FALSE(a->IsRuntimeArray());
   ASSERT_EQ(a->size(), 5u);
   ASSERT_TRUE(a->type()->Is<ast::F32>());
@@ -373,7 +357,7 @@
   auto* stride = a->decorations()[0];
   ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
   ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 16u}, {1u, 29u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 16u}, {1u, 29u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
@@ -381,11 +365,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
   ASSERT_TRUE(a->type()->Is<ast::F32>());
 
@@ -393,7 +377,7 @@
   auto* stride = a->decorations()[0];
   ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
   ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 16u);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 16u}, {1u, 26u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 16u}, {1u, 26u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
@@ -401,11 +385,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
   ASSERT_TRUE(a->type()->Is<ast::F32>());
 
@@ -415,7 +399,7 @@
   EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u);
   EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>());
   EXPECT_EQ(decos[1]->As<ast::StrideDecoration>()->stride(), 32u);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 28u}, {1u, 38u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 28u}, {1u, 38u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_MultipleBlocks) {
@@ -423,11 +407,11 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
   ASSERT_TRUE(a->type()->Is<ast::F32>());
 
@@ -437,7 +421,7 @@
   EXPECT_EQ(decos[0]->As<ast::StrideDecoration>()->stride(), 16u);
   EXPECT_TRUE(decos[1]->Is<ast::StrideDecoration>());
   EXPECT_EQ(decos[1]->As<ast::StrideDecoration>()->stride(), 32u);
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 31u}, {1u, 41u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 31u}, {1u, 41u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingArray) {
@@ -527,14 +511,14 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->As<ast::Array>();
   ASSERT_TRUE(a->IsRuntimeArray());
   ASSERT_TRUE(a->type()->Is<ast::U32>());
-  EXPECT_EQ(t.value.ast->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 11u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
@@ -542,14 +526,14 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  ASSERT_TRUE(t.value.ast->Is<ast::Array>());
+  ASSERT_TRUE(t.value->Is<ast::Array>());
 
-  auto* a = t.value.ast->As<ast::Array>();
+  auto* a = t.value->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}}));
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
@@ -641,13 +625,13 @@
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
+  ASSERT_NE(t.value, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
-  EXPECT_TRUE(t.value.ast->Is<ast::Matrix>());
-  auto* mat = t.value.ast->As<ast::Matrix>();
+  EXPECT_TRUE(t.value->Is<ast::Matrix>());
+  auto* mat = t.value->As<ast::Matrix>();
   EXPECT_EQ(mat->rows(), params.rows);
   EXPECT_EQ(mat->columns(), params.columns);
-  EXPECT_EQ(t.value.ast->source().range, params.range);
+  EXPECT_EQ(t.value->source().range, params.range);
 }
 INSTANTIATE_TEST_SUITE_P(
     ParserImplTest,
@@ -763,35 +747,26 @@
 TEST_F(ParserImplTest, TypeDecl_Sampler) {
   auto p = parser("sampler");
 
-  auto& builder = p->builder();
-  auto type = builder.ty.sampler(ast::SamplerKind::kSampler);
-
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr) << p->error();
-  EXPECT_EQ(t.value, type);
-  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}}));
+  ASSERT_NE(t.value, nullptr) << p->error();
+  ASSERT_TRUE(t.value->Is<ast::Sampler>());
+  ASSERT_FALSE(t.value->As<ast::Sampler>()->IsComparison());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 8u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Texture) {
   auto p = parser("texture_cube<f32>");
 
-  auto& builder = p->builder();
-  auto* type = builder.create<sem::SampledTexture>(ast::TextureDimension::kCube,
-                                                   ty.f32());
-
   auto t = p->type_decl();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
-  ASSERT_NE(t.value.ast, nullptr);
-  EXPECT_EQ(t.value, type);
-  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}}));
+  ASSERT_NE(t.value, nullptr);
+  ASSERT_TRUE(t.value->Is<ast::Texture>());
+  ASSERT_TRUE(t.value->Is<ast::SampledTexture>());
+  ASSERT_TRUE(t.value->As<ast::SampledTexture>()->type()->Is<ast::F32>());
+  EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 18u}}));
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl_variable_decl_test.cc b/src/reader/wgsl/parser_impl_variable_decl_test.cc
index d932bd1..bb5a659 100644
--- a/src/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -27,10 +27,10 @@
   EXPECT_FALSE(v.errored);
   EXPECT_EQ(v->name, "my_var");
   EXPECT_NE(v->type, nullptr);
-  EXPECT_TRUE(v->type->Is<sem::F32>());
+  EXPECT_TRUE(v->type->Is<ast::F32>());
 
   EXPECT_EQ(v->source.range, (Source::Range{{1u, 5u}, {1u, 11u}}));
-  EXPECT_EQ(v->type.ast->source().range, (Source::Range{{1u, 14u}, {1u, 17u}}));
+  EXPECT_EQ(v->type->source().range, (Source::Range{{1u, 14u}, {1u, 17u}}));
 }
 
 TEST_F(ParserImplTest, VariableDecl_MissingVar) {
@@ -60,7 +60,7 @@
   EXPECT_FALSE(v.errored);
   EXPECT_FALSE(p->has_error());
   EXPECT_EQ(v->name, "my_var");
-  EXPECT_TRUE(v->type->Is<sem::F32>());
+  EXPECT_TRUE(v->type->Is<ast::F32>());
   EXPECT_EQ(v->storage_class, ast::StorageClass::kPrivate);
 
   EXPECT_EQ(v->source.range.begin.line, 1u);
diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index 8d4ac62..55605b9 100644
--- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -28,11 +28,10 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<sem::F32>());
+  ASSERT_TRUE(decl->type->Is<ast::F32>());
 
   EXPECT_EQ(decl->source.range, (Source::Range{{1u, 1u}, {1u, 7u}}));
-  EXPECT_EQ(decl->type.ast->source().range,
-            (Source::Range{{1u, 10u}, {1u, 13u}}));
+  EXPECT_EQ(decl->type->source().range, (Source::Range{{1u, 10u}, {1u, 13u}}));
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
@@ -83,10 +82,10 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<sem::AccessControl>());
-  EXPECT_TRUE(decl->type->As<sem::AccessControl>()->IsReadOnly());
+  ASSERT_TRUE(decl->type->Is<ast::AccessControl>());
+  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsReadOnly());
   ASSERT_TRUE(
-      decl->type->As<sem::AccessControl>()->type()->Is<sem::StorageTexture>());
+      decl->type->As<ast::AccessControl>()->type()->Is<ast::StorageTexture>());
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithTextureAccessDeco_Write) {
@@ -97,10 +96,10 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<sem::AccessControl>());
-  EXPECT_TRUE(decl->type->As<sem::AccessControl>()->IsWriteOnly());
+  ASSERT_TRUE(decl->type->Is<ast::AccessControl>());
+  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsWriteOnly());
   ASSERT_TRUE(
-      decl->type->As<sem::AccessControl>()->type()->Is<sem::StorageTexture>());
+      decl->type->As<ast::AccessControl>()->type()->Is<ast::StorageTexture>());
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_Read) {
@@ -123,8 +122,8 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<sem::AccessControl>());
-  EXPECT_TRUE(decl->type->As<sem::AccessControl>()->IsReadOnly());
+  ASSERT_TRUE(decl->type->Is<ast::AccessControl>());
+  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsReadOnly());
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_ReadWrite) {
@@ -147,8 +146,8 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<sem::AccessControl>());
-  EXPECT_TRUE(decl->type->As<sem::AccessControl>()->IsReadWrite());
+  ASSERT_TRUE(decl->type->Is<ast::AccessControl>());
+  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsReadWrite());
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_MultipleAccessDecoFail) {