[tint][ir][val] Reject duplicate entry point names

Fixes: 381906257
Change-Id: If7ad29bd668bf3f590df9886d63d675e59c4d887
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/217715
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/transform/single_entry_point_test.cc b/src/tint/lang/core/ir/transform/single_entry_point_test.cc
index 650cb92..10f2ab9 100644
--- a/src/tint/lang/core/ir/transform/single_entry_point_test.cc
+++ b/src/tint/lang/core/ir/transform/single_entry_point_test.cc
@@ -100,28 +100,6 @@
     EXPECT_DEATH_IF_SUPPORTED({ Run(SingleEntryPoint, "foo"); }, "internal compiler error");
 }
 
-TEST_F(IR_SingleEntryPointTest, MultipleEntryPointsMatch) {
-    EntryPoint("main");
-    EntryPoint("main");
-
-    auto* src = R"(
-%main = @fragment func():void {
-  $B1: {
-    ret
-  }
-}
-%main_1 = @fragment func():void {  # %main_1: 'main'
-  $B2: {
-    ret
-  }
-}
-)";
-
-    EXPECT_EQ(src, str());
-
-    EXPECT_DEATH_IF_SUPPORTED({ Run(SingleEntryPoint, "main"); }, "internal compiler error");
-}
-
 TEST_F(IR_SingleEntryPointTest, NoChangesNeeded) {
     EntryPoint("main");
 
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index ba45fbd..4d789b9 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -1316,6 +1316,7 @@
     Hashset<const ir::Discard*, 4> discards_;
     core::ir::ReferencedModuleVars<const Module> referenced_module_vars_;
     Hashset<OverrideId, 8> seen_override_ids_;
+    Hashset<std::string, 4> entry_point_names_;
 
     Hashset<ValidatedType, 16> validated_types_{};
 };
@@ -1893,6 +1894,14 @@
     scope_stack_.Push();
     TINT_DEFER(scope_stack_.Pop());
 
+    // Checking the name early, so its usage can be recorded, even if the function is malformed.
+    if (func->Stage() != Function::PipelineStage::kUndefined) {
+        const auto name = mod_.NameOf(func).Name();
+        if (!entry_point_names_.Add(name)) {
+            AddError(func) << "entry point name " << style::Function(name) << " is not unique";
+        }
+    }
+
     if (!func->Type() || !func->Type()->Is<core::type::Function>()) {
         AddError(func) << "functions must have type '<function>'";
         return;
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 04381bb..74cf423 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -318,6 +318,34 @@
 )");
 }
 
+TEST_F(IR_ValidatorTest, Function_DuplicateEntryPointNames) {
+    auto* c = ComputeEntryPoint("dup");
+    c->Block()->Append(b.Return(c));
+
+    auto* f = FragmentEntryPoint("dup");
+    f->Block()->Append(b.Return(f));
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:6:1 error: entry point name 'dup' is not unique
+%dup_1 = @fragment func():void {  # %dup_1: 'dup'
+^^^^^^
+
+note: # Disassembly
+%dup = @compute @workgroup_size(1u, 1u, 1u) func():void {
+  $B1: {
+    ret
+  }
+}
+%dup_1 = @fragment func():void {  # %dup_1: 'dup'
+  $B2: {
+    ret
+  }
+}
+)");
+}
+
 TEST_F(IR_ValidatorTest, Function_MultinBlock) {
     auto* f = b.Function("my_func", ty.void_());
     f->SetBlock(b.MultiInBlock());