Add type alias unwrapping methods

Change-Id: I8dbd3bba48ae95d76f75a5eba3e97ed4e091ed01
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/23580
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/ast/expression.cc b/src/ast/expression.cc
index bbbc7ad..c1c9765 100644
--- a/src/ast/expression.cc
+++ b/src/ast/expression.cc
@@ -38,10 +38,7 @@
 
 void Expression::set_result_type(type::Type* type) {
   // The expression result should never be an alias type
-  while (type->IsAlias()) {
-    type = type->AsAlias()->type();
-  }
-  result_type_ = type;
+  result_type_ = type->UnwrapAliasesIfNeeded();
 }
 
 bool Expression::IsArrayAccessor() const {
diff --git a/src/ast/type/alias_type_test.cc b/src/ast/type/alias_type_test.cc
index 38d05b6..35b85f8 100644
--- a/src/ast/type/alias_type_test.cc
+++ b/src/ast/type/alias_type_test.cc
@@ -15,7 +15,9 @@
 #include "src/ast/type/alias_type.h"
 
 #include "gtest/gtest.h"
+#include "src/ast/storage_class.h"
 #include "src/ast/type/i32_type.h"
+#include "src/ast/type/pointer_type.h"
 #include "src/ast/type/u32_type.h"
 
 namespace tint {
@@ -54,6 +56,61 @@
   EXPECT_EQ(at.type_name(), "__alias_Particle__i32");
 }
 
+TEST_F(AliasTypeTest, UnwrapAliasesIfNeeded) {
+  U32Type u32;
+  AliasType a{"a_type", &u32};
+  EXPECT_EQ(a.name(), "a_type");
+  EXPECT_EQ(a.type(), &u32);
+  EXPECT_EQ(a.UnwrapAliasesIfNeeded(), &u32);
+  EXPECT_EQ(u32.UnwrapAliasesIfNeeded(), &u32);
+}
+
+TEST_F(AliasTypeTest, UnwrapAliasesIfNeeded_MultiLevel) {
+  U32Type u32;
+  AliasType a{"a_type", &u32};
+  AliasType aa{"aa_type", &a};
+  EXPECT_EQ(aa.name(), "aa_type");
+  EXPECT_EQ(aa.type(), &a);
+  EXPECT_EQ(aa.UnwrapAliasesIfNeeded(), &u32);
+}
+
+TEST_F(AliasTypeTest, UnwrapAliasPtrAlias_TwiceAliasPointerTwiceAlias) {
+  U32Type u32;
+  AliasType a{"a_type", &u32};
+  AliasType aa{"aa_type", &a};
+  PointerType paa{&aa, StorageClass::kUniform};
+  AliasType apaa{"paa_type", &paa};
+  AliasType aapaa{"aapaa_type", &apaa};
+  EXPECT_EQ(aapaa.name(), "aapaa_type");
+  EXPECT_EQ(aapaa.type(), &apaa);
+  EXPECT_EQ(aapaa.UnwrapAliasPtrAlias(), &u32);
+  EXPECT_EQ(u32.UnwrapAliasPtrAlias(), &u32);
+}
+
+TEST_F(AliasTypeTest,
+       UnwrapAliasPtrAlias_SecondConsecutivePointerBlocksWUnrapping) {
+  U32Type u32;
+  AliasType a{"a_type", &u32};
+  AliasType aa{"aa_type", &a};
+  PointerType paa{&aa, StorageClass::kUniform};
+  PointerType ppaa{&paa, StorageClass::kUniform};
+  AliasType appaa{"appaa_type", &ppaa};
+  EXPECT_EQ(appaa.UnwrapAliasPtrAlias(), &paa);
+}
+
+TEST_F(AliasTypeTest,
+       UnwrapAliasPtrAlias_SecondNonConsecutivePointerBlocksWUnrapping) {
+  U32Type u32;
+  AliasType a{"a_type", &u32};
+  AliasType aa{"aa_type", &a};
+  PointerType paa{&aa, StorageClass::kUniform};
+  AliasType apaa{"apaa_type", &paa};
+  AliasType aapaa{"aapaa_type", &apaa};
+  PointerType paapaa{&aapaa, StorageClass::kUniform};
+  AliasType apaapaa{"apaapaa_type", &paapaa};
+  EXPECT_EQ(apaapaa.UnwrapAliasPtrAlias(), &paa);
+}
+
 }  // namespace
 }  // namespace type
 }  // namespace ast
diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc
index a5b3ece..8475e9a 100644
--- a/src/ast/type/type.cc
+++ b/src/ast/type/type.cc
@@ -43,6 +43,18 @@
   return this;
 }
 
+Type* Type::UnwrapAliasesIfNeeded() {
+  auto* where = this;
+  while (where->IsAlias()) {
+    where = where->AsAlias()->type();
+  }
+  return where;
+}
+
+Type* Type::UnwrapAliasPtrAlias() {
+  return UnwrapAliasesIfNeeded()->UnwrapPtrIfNeeded()->UnwrapAliasesIfNeeded();
+}
+
 bool Type::IsAlias() const {
   return false;
 }
diff --git a/src/ast/type/type.h b/src/ast/type/type.h
index e368bad..b7086aa 100644
--- a/src/ast/type/type.h
+++ b/src/ast/type/type.h
@@ -69,6 +69,21 @@
   /// @returns the pointee type if this is a pointer, |this| otherwise
   Type* UnwrapPtrIfNeeded();
 
+  /// Removes all levels of aliasing, if this is an alias type.  Otherwise
+  /// returns |this|.  This is just enough to assist with WGSL translation
+  /// in that you want see through one level of pointer to get from an
+  /// identifier-like expression as an l-value to its corresponding r-value,
+  /// plus see through the aliases on either side.
+  /// @returns the completely unaliased type.
+  Type* UnwrapAliasesIfNeeded();
+
+  /// Returns the type found after:
+  /// - removing all layers of aliasing if they exist, then
+  /// - removing the pointer, if it exists, then
+  /// - removing all further layers of aliasing, if they exist
+  /// @returns the unwrapped type
+  Type* UnwrapAliasPtrAlias();
+
   /// @returns true if this type is a float scalar
   bool is_float_scalar();
   /// @returns true if this type is a float matrix
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 1a7dd00..d118b19 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -586,11 +586,7 @@
   }
 
   auto* res = expr->structure()->result_type();
-  auto* data_type = res->UnwrapPtrIfNeeded();
-
-  while (data_type->IsAlias()) {
-    data_type = data_type->AsAlias()->type();
-  }
+  auto* data_type = res->UnwrapPtrIfNeeded()->UnwrapAliasesIfNeeded();
 
   ast::type::Type* ret = nullptr;
   if (data_type->IsStruct()) {
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 586b045..0157837 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -642,10 +642,10 @@
 
 bool Builder::GenerateMemberAccessor(ast::MemberAccessorExpression* expr,
                                      AccessorInfo* info) {
-  auto* data_type = expr->structure()->result_type()->UnwrapPtrIfNeeded();
-  while (data_type->IsAlias()) {
-    data_type = data_type->AsAlias()->type();
-  }
+  auto* data_type = expr->structure()
+                        ->result_type()
+                        ->UnwrapPtrIfNeeded()
+                        ->UnwrapAliasesIfNeeded();
 
   // If the data_type is a structure we're accessing a member, if it's a
   // vector we're accessing a swizzle.