[spirv-reader] Use type aliases pervasively

When a type alias is created, map the SPIR-V type ID to the
type alias, not the underlying type. Only unpack the alias as
needed when inspecting the content structure to make values.

Bug: tint:3
Change-Id: I11011ddd190d89c81d3323f684a5e13f17dde09d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/23582
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/function_composite_test.cc b/src/reader/spirv/function_composite_test.cc
index 8d5cb70..f3caa24 100644
--- a/src/reader/spirv/function_composite_test.cc
+++ b/src/reader/spirv/function_composite_test.cc
@@ -218,10 +218,10 @@
   Variable{
     x_1
     none
-    __struct_S
+    __alias_S__struct_S
     {
       TypeConstructor{
-        __struct_S
+        __alias_S__struct_S
         TypeConstructor{
           __vec_2__f32
           ScalarConstructor{50.000000}
diff --git a/src/reader/spirv/function_var_test.cc b/src/reader/spirv/function_var_test.cc
index 1f929a7..dd9f283 100644
--- a/src/reader/spirv/function_var_test.cc
+++ b/src/reader/spirv/function_var_test.cc
@@ -431,7 +431,108 @@
     }
   }
 }
-)"));
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_ArrayInitializer_AliasType) {
+  auto* p = parser(test::Assemble(
+      std::string("OpDecorate %arr2uint ArrayStride 16\n") + CommonTypes() + R"(
+     %ptr = OpTypePointer Function %arr2uint
+     %two = OpConstant %uint 2
+     %const = OpConstantComposite %arr2uint %uint_1 %two
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %200 = OpVariable %ptr Function %const
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionVariables());
+
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
+  Variable{
+    x_200
+    function
+    __alias_Arr__array__u32_2_16
+    {
+      TypeConstructor{
+        __alias_Arr__array__u32_2_16
+        ScalarConstructor{1}
+        ScalarConstructor{2}
+      }
+    }
+  }
+}
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_ArrayInitializer_Null) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Function %arr2uint
+     %two = OpConstant %uint 2
+     %const = OpConstantNull %arr2uint
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %200 = OpVariable %ptr Function %const
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionVariables());
+
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
+  Variable{
+    x_200
+    function
+    __array__u32_2
+    {
+      TypeConstructor{
+        __array__u32_2
+        ScalarConstructor{0}
+        ScalarConstructor{0}
+      }
+    }
+  }
+}
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_ArrayInitializer_AliasType_Null) {
+  auto* p = parser(test::Assemble(
+      std::string("OpDecorate %arr2uint ArrayStride 16\n") + CommonTypes() + R"(
+     %ptr = OpTypePointer Function %arr2uint
+     %two = OpConstant %uint 2
+     %const = OpConstantNull %arr2uint
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %200 = OpVariable %ptr Function %const
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionVariables());
+
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
+  Variable{
+    x_200
+    function
+    __alias_Arr__array__u32_2_16
+    {
+      TypeConstructor{
+        __alias_Arr__array__u32_2_16
+        ScalarConstructor{0}
+        ScalarConstructor{0}
+      }
+    }
+  }
+}
+)")) << ToString(fe.ast_body());
 }
 
 TEST_F(SpvParserTest, EmitFunctionVariables_StructInitializer) {
@@ -455,10 +556,10 @@
   Variable{
     x_200
     function
-    __struct_S
+    __alias_S__struct_S
     {
       TypeConstructor{
-        __struct_S
+        __alias_S__struct_S
         ScalarConstructor{1}
         ScalarConstructor{1.500000}
         TypeConstructor{
@@ -470,7 +571,46 @@
     }
   }
 }
-)"));
+)")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvParserTest, EmitFunctionVariables_StructInitializer_Null) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Function %strct
+     %two = OpConstant %uint 2
+     %arrconst = OpConstantComposite %arr2uint %uint_1 %two
+     %const = OpConstantNull %strct
+
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %200 = OpVariable %ptr Function %const
+     OpReturn
+     OpFunctionEnd
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitFunctionVariables());
+
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
+  Variable{
+    x_200
+    function
+    __alias_S__struct_S
+    {
+      TypeConstructor{
+        __alias_S__struct_S
+        ScalarConstructor{0}
+        ScalarConstructor{0.000000}
+        TypeConstructor{
+          __array__u32_2
+          ScalarConstructor{0}
+          ScalarConstructor{0}
+        }
+      }
+    }
+  }
+}
+)")) << ToString(fe.ast_body());
 }
 
 }  // namespace
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index f5b896f..5d53d6d 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -274,10 +274,11 @@
     return nullptr;
   }
 
-  auto save = [this, type_id](ast::type::Type* type) {
+  auto save = [this, type_id, spirv_type](ast::type::Type* type) {
     if (type != nullptr) {
       id_to_type_[type_id] = type;
     }
+    MaybeGenerateAlias(spirv_type);
     return type;
   };
 
@@ -438,9 +439,6 @@
   if (!RegisterTypes()) {
     return false;
   }
-  if (!EmitAliasTypes()) {
-    return false;
-  }
   if (!EmitModuleScopeVariables()) {
     return false;
   }
@@ -757,52 +755,46 @@
   return success_;
 }
 
-bool ParserImpl::EmitAliasTypes() {
+void ParserImpl::MaybeGenerateAlias(const spvtools::opt::analysis::Type* type) {
   if (!success_) {
-    return false;
+    return;
   }
-  // The algorithm here emits type definitions in the order presented in
-  // the SPIR-V module.  This is valid because:
-  //
-  // - There are no back-references.  OpTypeForwarddPointer is not supported
-  //   by the WebGPU shader programming model.
-  // - Arrays are always sized by an OpConstant of scalar integral type.
-  //   WGSL currently doesn't have specialization constants.
-  //   crbug.com/32 tracks implementation in case they are added.
-  for (auto& type_or_const : module_->types_values()) {
-    const auto type_id = type_or_const.result_id();
-    // We only care about struct, arrays, and runtime arrays.
-    switch (type_or_const.opcode()) {
-      case SpvOpTypeStruct:
-        // The struct already got a name when the type was first registered.
-        break;
-      case SpvOpTypeRuntimeArray:
-        // Runtime arrays are always decorated with ArrayStride so always get a
-        // type alias.
-        namer_.SuggestSanitizedName(type_id, "RTArr");
-        break;
-      case SpvOpTypeArray:
-        // Only make a type aliase for arrays with decorations.
-        if (GetDecorationsFor(type_id).empty()) {
-          continue;
-        }
-        namer_.SuggestSanitizedName(type_id, "Arr");
-        break;
-      default:
-        // Ignore constants, and any other types.
-        continue;
-    }
-    auto* ast_underlying_type = id_to_type_[type_id];
-    if (ast_underlying_type == nullptr) {
-      Fail() << "internal error: no type registered for SPIR-V ID: " << type_id;
-      return false;
-    }
-    const auto name = namer_.GetName(type_id);
-    auto* ast_type = ctx_.type_mgr().Get(
-        std::make_unique<ast::type::AliasType>(name, ast_underlying_type));
-    ast_module_.AddAliasType(ast_type->AsAlias());
+  const auto type_id = type_mgr_->GetId(type);
+
+  // We only care about struct, arrays, and runtime arrays.
+  switch (type->kind()) {
+    case spvtools::opt::analysis::Type::kStruct:
+      // The struct already got a name when the type was first registered.
+      break;
+    case spvtools::opt::analysis::Type::kRuntimeArray:
+      // Runtime arrays are always decorated with ArrayStride so always get a
+      // type alias.
+      namer_.SuggestSanitizedName(type_id, "RTArr");
+      break;
+    case spvtools::opt::analysis::Type::kArray:
+      // Only make a type aliase for arrays with decorations.
+      if (GetDecorationsFor(type_id).empty()) {
+        return;
+      }
+      namer_.SuggestSanitizedName(type_id, "Arr");
+      break;
+    default:
+      // Ignore constants, and any other types.
+      return;
   }
-  return success_;
+  auto* ast_underlying_type = id_to_type_[type_id];
+  if (ast_underlying_type == nullptr) {
+    Fail() << "internal error: no type registered for SPIR-V ID: " << type_id;
+    return;
+  }
+  const auto name = namer_.GetName(type_id);
+  auto* ast_alias_type = ctx_.type_mgr()
+                             .Get(std::make_unique<ast::type::AliasType>(
+                                 name, ast_underlying_type))
+                             ->AsAlias();
+  // Record this new alias as the AST type for this SPIR-V ID.
+  id_to_type_[type_id] = ast_alias_type;
+  ast_module_.AddAliasType(ast_alias_type);
 }
 
 bool ParserImpl::EmitModuleScopeVariables() {
@@ -853,7 +845,6 @@
     Fail() << "internal error: can't make ast::Variable for null type";
     return nullptr;
   }
-
   auto ast_var = std::make_unique<ast::Variable>(namer_.Name(id), sc, type);
 
   ast::VariableDecorationList ast_decorations;
@@ -895,8 +886,8 @@
     Fail() << "ID " << id << " is not a registered instruction";
     return {};
   }
-  auto* ast_type = ConvertType(inst->type_id());
-  if (ast_type == nullptr) {
+  auto* original_ast_type = ConvertType(inst->type_id());
+  if (original_ast_type == nullptr) {
     return {};
   }
   // TODO(dneto): Handle spec constants too?
@@ -906,6 +897,8 @@
     return {};
   }
 
+  auto* ast_type = original_ast_type->UnwrapAliasesIfNeeded();
+
   // TODO(dneto): Note: NullConstant for int, uint, float map to a regular 0.
   // So canonicalization should map that way too.
   // Currently "null<type>" is missing from the WGSL parser.
@@ -955,12 +948,13 @@
       }
       ast_components.emplace_back(std::move(ast_component.expr));
     }
-    return {ast_type, std::make_unique<ast::TypeConstructorExpression>(
-                          ast_type, std::move(ast_components))};
+    return {original_ast_type,
+            std::make_unique<ast::TypeConstructorExpression>(
+                original_ast_type, std::move(ast_components))};
   }
   auto* spirv_null_const = spirv_const->AsNullConstant();
   if (spirv_null_const != nullptr) {
-    return {ast_type, MakeNullValue(ast_type)};
+    return {original_ast_type, MakeNullValue(original_ast_type)};
   }
   Fail() << "Unhandled constant type " << inst->type_id() << " for value ID "
          << id;
@@ -979,6 +973,9 @@
     return nullptr;
   }
 
+  auto* original_type = type;
+  type = type->UnwrapAliasesIfNeeded();
+
   if (type->IsBool()) {
     return std::make_unique<ast::ScalarConstructorExpression>(
         std::make_unique<ast::BoolLiteral>(type, false));
@@ -1024,7 +1021,7 @@
       ast_components.emplace_back(MakeNullValue(arr_ty->type()));
     }
     return std::make_unique<ast::TypeConstructorExpression>(
-        type, std::move(ast_components));
+        original_type, std::move(ast_components));
   }
   if (type->IsStruct()) {
     auto* struct_ty = type->AsStruct();
@@ -1033,7 +1030,7 @@
       ast_components.emplace_back(MakeNullValue(member->type()));
     }
     return std::make_unique<ast::TypeConstructorExpression>(
-        type, std::move(ast_components));
+        original_type, std::move(ast_components));
   }
   Fail() << "can't make null value for type: " << type->type_name();
   return nullptr;
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index 8c66bcb..b235b1b 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -35,6 +35,7 @@
 #include "src/ast/import.h"
 #include "src/ast/module.h"
 #include "src/ast/struct_member_decoration.h"
+#include "src/ast/type/alias_type.h"
 #include "src/ast/type/type.h"
 #include "src/reader/reader.h"
 #include "src/reader/spirv/enum_converter.h"
@@ -138,6 +139,16 @@
   /// @returns a Tint type, or nullptr
   ast::type::Type* ConvertType(uint32_t type_id);
 
+  /// Emits an alias type declaration for the given type, if necessary, and
+  /// also updates the mapping of the SPIR-V type ID to the alias type.
+  /// Do so for the types requiring user-specified names:
+  /// - struct types
+  /// - decorated arrays and runtime arrays
+  /// TODO(dneto): I expect images and samplers to require names as well.
+  /// This is a no-op if the parser has already failed.
+  /// @param type the type that might get an alias
+  void MaybeGenerateAlias(const spvtools::opt::analysis::Type* type);
+
   /// @returns the fail stream object
   FailStream& fail_stream() { return fail_stream_; }
   /// @returns the namer object
@@ -209,19 +220,11 @@
   /// @returns true if parser is still successful.
   bool EmitEntryPoints();
 
-  /// Register Tint AST types for SPIR-V types.
-  /// This is a no-op if the parser has already failed.
+  /// Register Tint AST types for SPIR-V types, including type aliases as
+  /// needed.  This is a no-op if the parser has already failed.
   /// @returns true if parser is still successful.
   bool RegisterTypes();
 
-  /// Emit type alias declarations for types requiring user-specified names:
-  /// - struct types
-  /// - decorated arrays and runtime arrays
-  /// TODO(dneto): I expect images and samplers to require names as well.
-  /// This is a no-op if the parser has already failed.
-  /// @returns true if parser is still successful.
-  bool EmitAliasTypes();
-
   /// Emits module-scope variables.
   /// This is a no-op if the parser has already failed.
   /// @returns true if parser is still successful.
@@ -377,7 +380,7 @@
   // sets.
   std::unordered_set<uint32_t> glsl_std_450_imports_;
 
-  // Maps a SPIR-V type ID to a Tint type.
+  // Maps a SPIR-V type ID to the corresponding Tint type.
   std::unordered_map<uint32_t, ast::type::Type*> id_to_type_;
 
   // Maps an unsigned type corresponding to the given signed type.
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index ef06c26..ffbe3cc 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -548,10 +548,10 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
-    __struct_S
+    __alias_S__struct_S
     {
       TypeConstructor{
-        __struct_S
+        __alias_S__struct_S
         ScalarConstructor{1}
         ScalarConstructor{1.500000}
         TypeConstructor{
@@ -561,7 +561,8 @@
         }
       }
     }
-  })"));
+  })"))
+      << module_str;
 }
 
 TEST_F(SpvParserTest, ModuleScopeVar_StructNullInitializer) {
@@ -570,16 +571,16 @@
      %const = OpConstantNull %strct
      %200 = OpVariable %ptr Private %const
   )"));
-  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
   EXPECT_TRUE(p->error().empty());
   const auto module_str = p->module().to_str();
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
-    __struct_S
+    __alias_S__struct_S
     {
       TypeConstructor{
-        __struct_S
+        __alias_S__struct_S
         ScalarConstructor{0}
         ScalarConstructor{0.000000}
         TypeConstructor{
@@ -589,7 +590,8 @@
         }
       }
     }
-  })"));
+  })"))
+      << module_str;
 }
 
 }  // namespace