Resolver: clean up how NamedTypes are handled

Also add validation for when a named type is declared more than once.

Bug: tint:803
Change-Id: Ifa93b34bc5afd31eba9bfdc4514791ec07c767ec
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51622
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@chromium.org>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index d05d058..fcfc908 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -212,28 +212,12 @@
 bool Resolver::ResolveInternal() {
   Mark(&builder_->AST());
 
-  auto register_named_type = [this](Symbol name, sem::Type* type,
-                                    const Source& source) {
-    auto added = named_types_.emplace(name, type).second;
-    if (!added) {
-      diagnostics_.add_error("type with the name '" +
-                                 builder_->Symbols().NameFor(name) +
-                                 "' was already declared",
-                             source);
-      return false;
-    }
-    return true;
-  };
-
   // Process everything else in the order they appear in the module. This is
   // necessary for validation of use-before-declaration.
   for (auto* decl : builder_->AST().GlobalDeclarations()) {
     if (auto* ty = decl->As<ast::NamedType>()) {
-      auto* sem_ty = Type(ty);
-      if (sem_ty == nullptr) {
-        return false;
-      }
-      if (!register_named_type(ty->name(), sem_ty, ty->source())) {
+      Mark(ty);
+      if (!NamedType(ty)) {
         return false;
       }
     } else if (auto* func = decl->As<ast::Function>()) {
@@ -299,14 +283,6 @@
     if (ty->Is<ast::F32>()) {
       return builder_->create<sem::F32>();
     }
-    if (auto* t = ty->As<ast::Alias>()) {
-      auto added = name_to_ast_type_.emplace(t->name(), t->type()).second;
-      // TODO(crbug.com/tint/803): Remove this.
-      if (!added) {
-        return nullptr;
-      }
-      return Type(t->type());
-    }
     if (auto* t = ty->As<ast::AccessControl>()) {
       TINT_SCOPED_ASSIGNMENT(curent_access_control_, t);
       if (auto* el = Type(t->type())) {
@@ -339,9 +315,6 @@
       }
       return nullptr;
     }
-    if (auto* t = ty->As<ast::Struct>()) {
-      return Structure(t);
-    }
     if (auto* t = ty->As<ast::Sampler>()) {
       return builder_->create<sem::Sampler>(t->kind());
     }
@@ -383,14 +356,14 @@
       return builder_->create<sem::ExternalTexture>();
     }
     if (auto* t = ty->As<ast::TypeName>()) {
-      auto it = named_types_.find(t->name());
-      if (it == named_types_.end()) {
+      auto it = named_type_info_.find(t->name());
+      if (it == named_type_info_.end()) {
         diagnostics_.add_error(
             "unknown type '" + builder_->Symbols().NameFor(t->name()) + "'",
             t->source());
         return nullptr;
       }
-      return it->second;
+      return it->second.sem;
     }
     TINT_UNREACHABLE(diagnostics_)
         << "Unhandled ast::Type: " << ty->TypeInfo().name;
@@ -488,19 +461,23 @@
 
   // TODO(crbug.com/tint/802): Temporary while ast::AccessControl exits.
   auto find_first_access_control =
-      [this](ast::Type* ty) -> ast::AccessControl* {
+      [this](const ast::Type* ty) -> const ast::AccessControl* {
     if (ty == nullptr) {
       return nullptr;
     }
-    if (ast::AccessControl* ac = ty->As<ast::AccessControl>()) {
+    if (const ast::AccessControl* ac = ty->As<ast::AccessControl>()) {
       return ac;
     }
     while (auto* tn = ty->As<ast::TypeName>()) {
-      auto it = name_to_ast_type_.find(tn->name());
-      if (it == name_to_ast_type_.end()) {
+      auto it = named_type_info_.find(tn->name());
+      if (it == named_type_info_.end()) {
         break;
       }
-      ty = it->second;
+      auto* alias = it->second.ast->As<ast::Alias>();
+      if (!alias) {
+        break;
+      }
+      ty = alias->type();
       if (auto* ac = ty->As<ast::AccessControl>()) {
         return ac;
       }
@@ -2425,6 +2402,47 @@
   return true;
 }
 
+sem::Type* Resolver::NamedType(const ast::NamedType* named_type) {
+  sem::Type* result = nullptr;
+  if (auto* alias = named_type->As<ast::Alias>()) {
+    result = Type(alias->type());
+  } else if (auto* str = named_type->As<ast::Struct>()) {
+    result = Structure(str);
+  } else {
+    TINT_UNREACHABLE(diagnostics_) << "Unhandled NamedType";
+  }
+
+  if (!result) {
+    return nullptr;
+  }
+
+  named_type_info_.emplace(named_type->name(),
+                           NamedTypeInfo{named_type, result});
+
+  if (!ValidateNamedType(named_type)) {
+    return nullptr;
+  }
+
+  builder_->Sem().Add(named_type, result);
+  return result;
+}
+
+bool Resolver::ValidateNamedType(const ast::NamedType* named_type) const {
+  auto iter = named_type_info_.find(named_type->name());
+  if (iter == named_type_info_.end()) {
+    TINT_ICE(diagnostics_) << "ValidateNamedType called() before NamedType()";
+  }
+  if (iter->second.ast != named_type) {
+    diagnostics_.add_error("type with the name '" +
+                               builder_->Symbols().NameFor(named_type->name()) +
+                               "' was already declared",
+                           named_type->source());
+    diagnostics_.add_note("first declared here", iter->second.ast->source());
+    return false;
+  }
+  return true;
+}
+
 sem::Type* Resolver::TypeOf(const ast::Expression* expr) {
   auto it = expr_info_.find(expr);
   if (it != expr_info_.end()) {
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index 8a622d2..ac2bbf0 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -169,6 +169,12 @@
     size_t first_continue = kNoContinue;
   };
 
+  // Structure holding information for a NamedType
+  struct NamedTypeInfo {
+    ast::NamedType const* const ast;
+    sem::Type* const sem;
+  };
+
   /// Describes the context in which a variable is declared
   enum class VariableKind { kParameter, kLocal, kGlobal };
 
@@ -247,6 +253,7 @@
                                    const std::string& rhs_type_name);
   bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
                                  const sem::Vector* vec_type);
+  bool ValidateNamedType(const ast::NamedType* named_type) const;
 
   /// @returns the sem::Type for the ast::Type `ty`, building it if it
   /// hasn't been constructed already. If an error is raised, nullptr is
@@ -254,6 +261,10 @@
   /// @param ty the ast::Type
   sem::Type* Type(const ast::Type* ty);
 
+  /// @param named_type the named type to resolve
+  /// @returns the resolved semantic type
+  sem::Type* NamedType(const ast::NamedType* named_type);
+
   /// Builds and returns the semantic information for the array `arr`.
   /// This method does not mark the ast::Array node, nor attach the generated
   /// semantic information to the AST node.
@@ -358,10 +369,10 @@
   std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
   std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
   std::unordered_map<const ast::Expression*, ExpressionInfo> expr_info_;
-  std::unordered_map<Symbol, sem::Type*> named_types_;
+  std::unordered_map<Symbol, NamedTypeInfo> named_type_info_;
+
   std::unordered_set<const ast::Node*> marked_;
   std::unordered_map<uint32_t, const VariableInfo*> constant_ids_;
-  std::unordered_map<Symbol, ast::Type*> name_to_ast_type_;
 
   FunctionInfo* current_function_ = nullptr;
   sem::Statement* current_statement_ = nullptr;
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 8e5631b..53fb0b7 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -277,6 +277,19 @@
   EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
 }
 
+TEST_F(ResolverTest, Stmt_VariableDecl_AliasRedeclared) {
+  auto* my_int1 = ty.alias(Source{{12, 34}}, "MyInt", ty.i32());
+  auto* my_int2 = ty.alias(Source{{56, 78}}, "MyInt", ty.i32());
+  AST().AddConstructedType(my_int1);
+  AST().AddConstructedType(my_int2);
+  WrapInFunction();
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "56:78 error: type with the name 'MyInt' was already declared\n"
+            "12:34 note: first declared here");
+}
+
 TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
   auto* init = Expr(2);
   Global("my_var", ty.i32(), ast::StorageClass::kInput, init);