wgsl parser: make type_decl() return typ::Type

Many more changes to come, but this just gets things started.

Bug: tint:724
Change-Id: I6c1dab0d9b7998e273543f2a72b6f8eafeb19fda
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48840
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index f09b481..58f54fb 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -167,6 +167,17 @@
 
 }  // namespace
 
+ParserImpl::TypedIdentifier::TypedIdentifier() = default;
+
+ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default;
+
+ParserImpl::TypedIdentifier::TypedIdentifier(typ::Type type_in,
+                                             std::string name_in,
+                                             Source source_in)
+    : type(type_in), name(std::move(name_in)), source(std::move(source_in)) {}
+
+ParserImpl::TypedIdentifier::~TypedIdentifier() = default;
+
 ParserImpl::FunctionHeader::FunctionHeader() = default;
 
 ParserImpl::FunctionHeader::FunctionHeader(const FunctionHeader&) = default;
@@ -840,15 +851,26 @@
   if (access_decos.size() > 1)
     return add_error(ident.source, "multiple access decorations not allowed");
 
-  auto* ty = type.value;
+  // TODO(crbug.com/tint/724): Remove
+  auto* sem_ty = type.value.sem;
   for (auto* deco : access_decos) {
     // If we have an access control decoration then we take it and wrap our
     // type up with that decoration
-    ty = builder_.create<sem::AccessControl>(
-        deco->As<ast::AccessDecoration>()->value(), ty);
+    sem_ty = builder_.create<sem::AccessControl>(
+        deco->As<ast::AccessDecoration>()->value(), sem_ty);
   }
 
-  return TypedIdentifier{ty, ident.value, ident.source};
+  auto* ty = type.value.ast;
+  // TODO(crbug.com/tint/724): Remove 'if'
+  if (ty) {
+    for (auto* deco : access_decos) {
+      // If we have an access control decoration then we take it and wrap our
+      // type up with that decoration
+      ty = builder_.create<ast::AccessControl>(
+          deco->As<ast::AccessDecoration>()->value(), ty);
+    }
+  }
+  return TypedIdentifier{typ::Type{ty, sem_ty}, ident.value, ident.source};
 }
 
 Expect<ast::AccessControl::Access> ParserImpl::expect_access_type() {
@@ -937,7 +959,7 @@
 //   | MAT4x3 LESS_THAN type_decl GREATER_THAN
 //   | MAT4x4 LESS_THAN type_decl GREATER_THAN
 //   | texture_sampler_types
-Maybe<sem::Type*> ParserImpl::type_decl() {
+Maybe<typ::Type> ParserImpl::type_decl() {
   auto decos = decoration_list();
   if (decos.errored)
     return Failure::kErrored;
@@ -951,55 +973,56 @@
   if (!expect_decorations_consumed(decos.value))
     return Failure::kErrored;
 
-  return type.value;
+  return type;
 }
 
-Maybe<sem::Type*> ParserImpl::type_decl(ast::DecorationList& decos) {
+Maybe<typ::Type> ParserImpl::type_decl(ast::DecorationList& decos) {
   auto t = peek();
   if (match(Token::Type::kIdentifier)) {
     auto* ty = get_constructed(t.to_str());
     if (ty == nullptr)
       return add_error(t, "unknown constructed type '" + t.to_str() + "'");
 
-    return ty;
+    // TODO(crbug.com/tint/724): builder_.create<ast::TypeName>(t.to_str())
+    return typ::Type{nullptr, ty};
   }
 
   if (match(Token::Type::kBool))
-    return builder_.create<sem::Bool>();
+    return typ::Type{nullptr, builder_.create<sem::Bool>()};
 
   if (match(Token::Type::kF32))
-    return builder_.create<sem::F32>();
+    return typ::Type{nullptr, builder_.create<sem::F32>()};
 
   if (match(Token::Type::kI32))
-    return builder_.create<sem::I32>();
+    return typ::Type{nullptr, builder_.create<sem::I32>()};
 
   if (match(Token::Type::kU32))
-    return builder_.create<sem::U32>();
+    return typ::Type{nullptr, builder_.create<sem::U32>()};
 
   if (t.IsVec2() || t.IsVec3() || t.IsVec4()) {
     next();  // Consume the peek
-    return expect_type_decl_vector(t);
+    return from_deprecated(expect_type_decl_vector(t));
   }
 
   if (match(Token::Type::kPtr))
-    return expect_type_decl_pointer();
+    return from_deprecated(expect_type_decl_pointer());
 
   if (match(Token::Type::kArray)) {
-    return expect_type_decl_array(std::move(decos));
+    return from_deprecated(expect_type_decl_array(std::move(decos)));
   }
 
   if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
       t.IsMat3x3() || t.IsMat3x4() || t.IsMat4x2() || t.IsMat4x3() ||
       t.IsMat4x4()) {
     next();  // Consume the peek
-    return expect_type_decl_matrix(t);
+    return from_deprecated(expect_type_decl_matrix(t));
   }
 
   auto texture_or_sampler = texture_sampler_types();
   if (texture_or_sampler.errored)
     return Failure::kErrored;
   if (texture_or_sampler.matched)
-    return texture_or_sampler.value;
+    return typ::Type{nullptr, texture_or_sampler.value};
 
   return Failure::kNoMatch;
 }
@@ -1244,7 +1267,7 @@
   if (match(Token::Type::kVoid))
     return builder_.create<sem::Void>();
 
-  return type_decl();
+  return to_deprecated(type_decl());
 }
 
 // function_header
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 6b01db2..871f86e 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -36,6 +36,7 @@
 #include "src/reader/wgsl/parser_impl_detail.h"
 #include "src/reader/wgsl/token.h"
 #include "src/sem/storage_texture_type.h"
+#include "src/typepair.h"
 
 namespace tint {
 namespace reader {
@@ -198,8 +199,21 @@
   /// TypedIdentifier holds a parsed identifier and type. Returned by
   /// variable_ident_decl().
   struct TypedIdentifier {
+    /// Constructor
+    TypedIdentifier();
+    /// Copy constructor
+    /// @param other the FunctionHeader to copy
+    TypedIdentifier(const TypedIdentifier& other);
+    /// Constructor
+    /// @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);
+    /// Destructor
+    ~TypedIdentifier();
+
     /// Parsed type.
-    sem::Type* type = nullptr;
+    typ::Type type;
     /// Parsed identifier.
     std::string name;
     /// Source to the identifier.
@@ -365,12 +379,18 @@
   Maybe<sem::Type*> type_alias();
   /// Parses a `type_decl` grammar element
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<sem::Type*> type_decl();
+  Maybe<typ::Type> type_decl();
+  /// TODO(crbug.com/tint/724): Temporary until type_decl() returns
+  /// Maybe<ast::Type*>
+  /// @returns the parsed Type or nullptr if none matched.
+  Maybe<sem::Type*> type_decl_DEPRECATED() {
+    return to_deprecated(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<sem::Type*> type_decl(ast::DecorationList& decos);
+  Maybe<typ::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
@@ -635,6 +655,30 @@
   Expect<ast::Decoration*> expect_decoration();
 
  private:
+  // TODO(crbug.com/tint/724): Helper to convert Maybe<typ::Type> to
+  // Maybe<sem::Type*> while we convert code
+  Maybe<sem::Type*> to_deprecated(const Maybe<typ::Type>& tp) {
+    if (tp.errored) {
+      return Failure::kErrored;
+    }
+    if (!tp.matched) {
+      return Failure::kNoMatch;
+    }
+    return tp.value;
+  }
+
+  //// TODO(crbug.com/tint/724): Helper to convert Maybe<sem::Type*> to
+  /// Maybe<typ::Type> while we / convert code
+  Maybe<typ::Type> from_deprecated(const Maybe<sem::Type*>& tp) {
+    if (tp.errored) {
+      return Failure::kErrored;
+    }
+    if (!tp.matched) {
+      return Failure::kNoMatch;
+    }
+    return typ::Type{nullptr, tp.value};
+  }
+
   /// ReturnType resolves to the return type for the function or lambda F.
   template <typename F>
   using ReturnType = typename std::result_of<F()>::type;
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index 185ac69..3d61b15 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -22,7 +22,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Invalid) {
   auto p = parser("1234");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_EQ(t.errored, false);
   EXPECT_EQ(t.matched, false);
   EXPECT_EQ(t.value, nullptr);
@@ -40,7 +40,7 @@
 
   p->register_constructed("A", alias_type);
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -55,7 +55,7 @@
 TEST_F(ParserImplTest, TypeDecl_Identifier_NotFound) {
   auto p = parser("B");
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -69,7 +69,7 @@
   auto& builder = p->builder();
   auto* bool_type = builder.create<sem::Bool>();
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -83,7 +83,7 @@
   auto& builder = p->builder();
   auto* float_type = builder.create<sem::F32>();
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -97,7 +97,7 @@
   auto& builder = p->builder();
   auto* int_type = builder.create<sem::I32>();
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -111,7 +111,7 @@
   auto& builder = p->builder();
   auto* uint_type = builder.create<sem::U32>();
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -133,7 +133,7 @@
 TEST_P(VecTest, Parse) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -152,7 +152,7 @@
 TEST_P(VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -170,7 +170,7 @@
 TEST_P(VecMissingLessThanTest, Handles_Missing_GreaterThan) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -188,7 +188,7 @@
 TEST_P(VecBadType, Handles_Unknown_Type) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -206,7 +206,7 @@
 TEST_P(VecMissingType, Handles_Missing_Type) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -221,7 +221,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr) {
   auto p = parser("ptr<function, f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -235,7 +235,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
   auto p = parser("ptr<function, vec2<f32>>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -253,7 +253,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingLessThan) {
   auto p = parser("ptr private, f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -263,7 +263,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingGreaterThan) {
   auto p = parser("ptr<function, f32");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -273,7 +273,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingComma) {
   auto p = parser("ptr<function f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -283,7 +283,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingStorageClass) {
   auto p = parser("ptr<, f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -293,7 +293,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingParams) {
   auto p = parser("ptr<>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -303,7 +303,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingType) {
   auto p = parser("ptr<function,>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -313,7 +313,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) {
   auto p = parser("ptr<unknown, f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -323,7 +323,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_BadType) {
   auto p = parser("ptr<function, unknown>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -333,7 +333,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array) {
   auto p = parser("array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -349,7 +349,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
   auto p = parser("[[stride(16)]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -369,7 +369,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
   auto p = parser("[[stride(16)]] array<f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -388,7 +388,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_OneBlock) {
   auto p = parser("[[stride(16), stride(32)]] array<f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -409,7 +409,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MultipleDecorations_MultipleBlocks) {
   auto p = parser("[[stride(16)]] [[stride(32)]] array<f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -430,7 +430,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingArray) {
   auto p = parser("[[stride(16)]] f32");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -440,7 +440,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingClosingAttr) {
   auto p = parser("[[stride(16) array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -450,7 +450,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Decoration_UnknownDecoration) {
   auto p = parser("[[unknown 16]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -460,7 +460,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingLeftParen) {
   auto p = parser("[[stride 4)]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -470,7 +470,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingRightParen) {
   auto p = parser("[[stride(4]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -480,7 +480,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingValue) {
   auto p = parser("[[stride()]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -491,7 +491,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_InvalidValue) {
   auto p = parser("[[stride(invalid)]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -502,7 +502,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_InvalidValue_Negative) {
   auto p = parser("[[stride(-1)]] array<f32, 5>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -512,7 +512,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime) {
   auto p = parser("array<u32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -526,7 +526,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
   auto p = parser("array<vec4<u32>>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -540,7 +540,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
   auto p = parser("array<unknown, 3>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -550,7 +550,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_ZeroSize) {
   auto p = parser("array<f32, 0>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -560,7 +560,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_NegativeSize) {
   auto p = parser("array<f32, -1>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -570,7 +570,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_BadSize) {
   auto p = parser("array<f32, invalid>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -580,7 +580,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MissingLessThan) {
   auto p = parser("array f32>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -590,7 +590,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MissingGreaterThan) {
   auto p = parser("array<f32");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -600,7 +600,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MissingComma) {
   auto p = parser("array<f32 3>");
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -623,7 +623,7 @@
 TEST_P(MatrixTest, Parse) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -651,7 +651,7 @@
 TEST_P(MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -675,7 +675,7 @@
 TEST_P(MatrixMissingLessThanTest, Handles_Missing_GreaterThan) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -699,7 +699,7 @@
 TEST_P(MatrixBadType, Handles_Unknown_Type) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -723,7 +723,7 @@
 TEST_P(MatrixMissingType, Handles_Missing_Type) {
   auto params = GetParam();
   auto p = parser(params.input);
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.errored);
   EXPECT_FALSE(t.matched);
   ASSERT_EQ(t.value, nullptr);
@@ -748,7 +748,7 @@
   auto& builder = p->builder();
   auto type = builder.ty.sampler(ast::SamplerKind::kSampler);
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr) << p->error();
@@ -764,7 +764,7 @@
   auto* type = builder.create<sem::SampledTexture>(ast::TextureDimension::kCube,
                                                    ty.f32());
 
-  auto t = p->type_decl();
+  auto t = p->type_decl_DEPRECATED();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   ASSERT_NE(t.value, nullptr);
diff --git a/src/typepair.h b/src/typepair.h
index 8f34925..f67494c 100644
--- a/src/typepair.h
+++ b/src/typepair.h
@@ -36,6 +36,7 @@
 class SampledTexture;
 class StorageTexture;
 class Struct;
+class Type;
 class U32;
 class Vector;
 class Void;
@@ -56,6 +57,7 @@
 class SampledTexture;
 class StorageTexture;
 class StructType;
+class Type;
 class U32;
 class Vector;
 class Void;
@@ -112,6 +114,10 @@
   /// @returns true if the semantic type is equal to `ty`
   bool operator==(sem::Type* ty) const { return sem == ty; }
 
+  /// @param ty the semantic type to compare against
+  /// @returns true if the semantic type is not equal to `ty`
+  bool operator!=(sem::Type* ty) const { return !((*this) == ty); }
+
   /// @param other the TypePair to compare against
   /// @returns true if this TypePair is less than `other`
   template <typename OTHER_AST, typename OTHER_SEM>