[validation] Update entry points validation v-0020

Remove irrelevant unit tests
Add/Update ValdiateEntryPoints function
Update known failure file

Bug: tint:296
Change-Id: I7d5c9c96fcca29f3e0a4c0315eb8ce869160a3ea
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32220
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: David Neto <dneto@google.com>
diff --git a/src/validator/validator_function_test.cc b/src/validator/validator_function_test.cc
index 15ec76a..5f60f85 100644
--- a/src/validator/validator_function_test.cc
+++ b/src/validator/validator_function_test.cc
@@ -316,65 +316,27 @@
             "'vtx_func'");
 }
 
-TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Fail) {
-  // [[stage(vertex)]]
-  // fn main() -> void { return ;}
-  // [[stage(vertex)]]
-  // fn main() -> void { return; }
-  ast::type::VoidType void_type;
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>(
-      Source{Source::Location{5, 6}}, "main", std::move(params), &void_type);
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-  func->add_decoration(std::make_unique<ast::StageDecoration>(
-      ast::PipelineStage::kVertex, Source{}));
-  mod()->AddFunction(std::move(func));
-
-  func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
-                                         "main", std::move(params), &void_type);
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-  func->add_decoration(std::make_unique<ast::StageDecoration>(
-      ast::PipelineStage::kVertex, Source{}));
-  mod()->AddFunction(std::move(func));
-
-  EXPECT_TRUE(td()->Determine()) << td()->error();
-  EXPECT_FALSE(v()->Validate(mod()));
-  EXPECT_EQ(v()->error(),
-            "12:34: v-0020: The pair of <entry point name, pipeline stage> "
-            "must be unique");
-}
-
-TEST_F(ValidateFunctionTest, PipelineStageNamePair_MustBeUnique_Pass) {
-  // [[stage(vertex)]]
-  // fn main() -> void { return; }
+TEST_F(ValidateFunctionTest, PipelineStage_MustBeUnique_Fail) {
   // [[stage(fragment)]]
+  // [[stage(vertex)]]
   // fn main() -> void { return; }
   ast::type::VoidType void_type;
   ast::VariableList params;
   auto func = std::make_unique<ast::Function>(
-      Source{Source::Location{5, 6}}, "main", std::move(params), &void_type);
+      Source{Source::Location{12, 34}}, "main", std::move(params), &void_type);
   auto body = std::make_unique<ast::BlockStatement>();
   body->append(std::make_unique<ast::ReturnStatement>());
   func->set_body(std::move(body));
   func->add_decoration(std::make_unique<ast::StageDecoration>(
       ast::PipelineStage::kVertex, Source{}));
-  mod()->AddFunction(std::move(func));
-
-  func = std::make_unique<ast::Function>(Source{Source::Location{12, 34}},
-                                         "main", std::move(params), &void_type);
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
   func->add_decoration(std::make_unique<ast::StageDecoration>(
       ast::PipelineStage::kFragment, Source{}));
   mod()->AddFunction(std::move(func));
-
   EXPECT_TRUE(td()->Determine()) << td()->error();
-  EXPECT_TRUE(v()->Validate(mod())) << v()->error();
+  EXPECT_FALSE(v()->Validate(mod()));
+  EXPECT_EQ(
+      v()->error(),
+      "12:34: v-0020: only one stage decoration permitted per entry point");
 }
 
 TEST_F(ValidateFunctionTest, OnePipelineStageFunctionMustBePresent_Pass) {
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 1f6c01e..92bbb58 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -51,7 +51,9 @@
   if (!ValidateFunctions(module->functions())) {
     return false;
   }
-
+  if (!ValidateEntryPoint(module->functions())) {
+    return false;
+  }
   function_stack_.pop_scope();
 
   return true;
@@ -82,49 +84,13 @@
 }
 
 bool ValidatorImpl::ValidateFunctions(const ast::FunctionList& funcs) {
-  ScopeStack<ast::PipelineStage> entry_point_map;
-  entry_point_map.push_scope();
-
-  size_t pipeline_count = 0;
   for (const auto& func : funcs) {
-    // The entry points will be checked later to see if their duplicated
-    if (function_stack_.has(func->name()) &&
-        !entry_point_map.has(func->name())) {
+    if (function_stack_.has(func->name())) {
       set_error(func->source(),
                 "v-0016: function names must be unique '" + func->name() + "'");
       return false;
     }
 
-    if (func->IsEntryPoint()) {
-      pipeline_count++;
-
-      if (!func->return_type()->IsVoid()) {
-        set_error(func->source(),
-                  "v-0024: Entry point function must return void: '" +
-                      func->name() + "'");
-        return false;
-      }
-
-      if (func->params().size() != 0) {
-        set_error(func->source(),
-                  "v-0023: Entry point function must accept no parameters: '" +
-                      func->name() + "'");
-        return false;
-      }
-
-      ast::PipelineStage pipeline_stage;
-      if (entry_point_map.get(func->name(), &pipeline_stage)) {
-        if (pipeline_stage == func->pipeline_stage()) {
-          set_error(
-              func->source(),
-              "v-0020: The pair of <entry point name, pipeline stage> must "
-              "be unique");
-          return false;
-        }
-      }
-      entry_point_map.set(func->name(), func->pipeline_stage());
-    }
-
     function_stack_.set(func->name(), func.get());
     current_function_ = func.get();
     if (!ValidateFunction(func.get())) {
@@ -133,13 +99,47 @@
     current_function_ = nullptr;
   }
 
-  if (pipeline_count == 0) {
+  return true;
+}
+
+bool ValidatorImpl::ValidateEntryPoint(const ast::FunctionList& funcs) {
+  auto shader_is_present = false;
+  for (const auto& func : funcs) {
+    if (func->IsEntryPoint()) {
+      shader_is_present = true;
+      if (!func->params().empty()) {
+        set_error(func->source(),
+                  "v-0023: Entry point function must accept no parameters: '" +
+                      func->name() + "'");
+        return false;
+      }
+
+      if (!func->return_type()->IsVoid()) {
+        set_error(func->source(),
+                  "v-0024: Entry point function must return void: '" +
+                      func->name() + "'");
+        return false;
+      }
+      auto stage_deco_count = 0;
+      for (const auto& deco : func->decorations()) {
+        if (deco->IsStage()) {
+          stage_deco_count++;
+        }
+      }
+      if (stage_deco_count > 1) {
+        set_error(
+            func->source(),
+            "v-0020: only one stage decoration permitted per entry point");
+        return false;
+      }
+    }
+  }
+  if (!shader_is_present) {
     set_error(Source{},
               "v-0003: At least one of vertex, fragment or compute shader must "
               "be present");
     return false;
   }
-
   return true;
 }
 
diff --git a/src/validator/validator_impl.h b/src/validator/validator_impl.h
index bfa1c9b..a406f53 100644
--- a/src/validator/validator_impl.h
+++ b/src/validator/validator_impl.h
@@ -113,6 +113,10 @@
   /// @param c the case statement to check
   /// @returns true if the valdiation was successful
   bool ValidateCase(const ast::CaseStatement* c);
+  /// Validates entry points
+  /// @param funcs the functions to check
+  /// @returns true if the valdiation was successful
+  bool ValidateEntryPoint(const ast::FunctionList& funcs);
 
  private:
   std::string error_;
diff --git a/tools/known_tint_failures b/tools/known_tint_failures
index 13dbaaf..8cd01f1 100644
--- a/tools/known_tint_failures
+++ b/tools/known_tint_failures
@@ -1,6 +1,5 @@
 break-outside-for-or-switch.fail.wgsl
 continue-outside-for.fail.wgsl
-duplicate-entry-point.fail.wgsl
 duplicate-stuct-name-v2.fail.wgsl
 duplicate-stuct-name.fail.wgsl
 duplicate-var-name-within-func.fail.wgsl