Add type::Type::UnwrapAliasIfNeeded()

Unwraps aliased types until reaching a non-alias.

Change-Id: I6546d60b7cbe07d4c8cc5a0b439329af8b468ca9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41500
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index 74c2242..f43d6e9 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -96,10 +96,7 @@
   /// Match may add to, or compare against the open types and numbers in state.
   /// @returns true if the argument type is as expected.
   bool Match(MatchState& state, type::Type* argument_type) const {
-    auto* unwrapped = argument_type;
-    while (auto* alias = unwrapped->As<type::Alias>()) {
-      unwrapped = alias->type();
-    }
+    auto* unwrapped = argument_type->UnwrapAliasIfNeeded();
     return MatchUnwrapped(state, unwrapped);
   }
 
diff --git a/src/type/alias_type_test.cc b/src/type/alias_type_test.cc
index d39b201..470f390 100644
--- a/src/type/alias_type_test.cc
+++ b/src/type/alias_type_test.cc
@@ -225,6 +225,13 @@
   EXPECT_EQ(4u, alias->BaseAlignment(MemoryLayout::kStorageBuffer));
 }
 
+TEST_F(AliasTest, UnwrapAliasIfNeeded) {
+  auto* alias1 = ty.alias("alias1", ty.f32());
+  auto* alias2 = ty.alias("alias2", alias1);
+  auto* alias3 = ty.alias("alias3", alias2);
+  EXPECT_EQ(alias3->UnwrapAliasIfNeeded(), ty.f32());
+}
+
 }  // namespace
 }  // namespace type
 }  // namespace tint
diff --git a/src/type/type.cc b/src/type/type.cc
index 679e4e3..2f089a0 100644
--- a/src/type/type.cc
+++ b/src/type/type.cc
@@ -49,6 +49,14 @@
   return this;
 }
 
+Type* Type::UnwrapAliasIfNeeded() {
+  Type* unwrapped = this;
+  while (auto* ptr = unwrapped->As<type::Alias>()) {
+    unwrapped = ptr->type();
+  }
+  return unwrapped;
+}
+
 Type* Type::UnwrapIfNeeded() {
   auto* where = this;
   while (true) {
diff --git a/src/type/type.h b/src/type/type.h
index 7c747c9..2cb4c21 100644
--- a/src/type/type.h
+++ b/src/type/type.h
@@ -64,6 +64,10 @@
   /// @returns the pointee type if this is a pointer, `this` otherwise
   Type* UnwrapPtrIfNeeded();
 
+  /// @returns the most deeply nested aliased type if this is an alias, `this`
+  /// otherwise
+  Type* UnwrapAliasIfNeeded();
+
   /// Removes all levels of aliasing and access control.
   /// This is just enough to assist with WGSL translation
   /// in that you want see through one level of pointer to get from an