validator: Validate attributes on non-entry point functions

Also validate that workgroup_size is only applied to compute stages,
and not duplicated.

Fixed: tint:703
Change-Id: I02f4ddea305cad25ee0a99e13dc9e7fd1d5dc3ea
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51120
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: James Price <jrprice@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index d5166d9..145bdc0 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -823,6 +823,38 @@
     return false;
   }
 
+  auto stage_deco_count = 0;
+  auto workgroup_deco_count = 0;
+  for (auto* deco : func->decorations()) {
+    if (deco->Is<ast::StageDecoration>()) {
+      stage_deco_count++;
+    } else if (deco->Is<ast::WorkgroupDecoration>()) {
+      workgroup_deco_count++;
+      if (func->pipeline_stage() != ast::PipelineStage::kCompute) {
+        diagnostics_.add_error(
+            "the workgroup_size attribute is only valid for compute stages",
+            deco->source());
+        return false;
+      }
+    } else if (!deco->Is<ast::InternalDecoration>()) {
+      diagnostics_.add_error("decoration is not valid for functions",
+                             deco->source());
+      return false;
+    }
+  }
+  if (stage_deco_count > 1) {
+    diagnostics_.add_error(
+        "v-0020", "only one stage decoration permitted per entry point",
+        func->source());
+    return false;
+  }
+  if (workgroup_deco_count > 1) {
+    diagnostics_.add_error(
+        "only one workgroup_size attribute permitted per entry point",
+        func->source());
+    return false;
+  }
+
   for (auto* param : func->params()) {
     if (!ValidateParameter(variable_to_info_.at(param))) {
       return false;
@@ -867,23 +899,6 @@
 
 bool Resolver::ValidateEntryPoint(const ast::Function* func,
                                   const FunctionInfo* info) {
-  auto stage_deco_count = 0;
-  for (auto* deco : func->decorations()) {
-    if (deco->Is<ast::StageDecoration>()) {
-      stage_deco_count++;
-    } else if (!deco->Is<ast::WorkgroupDecoration>()) {
-      diagnostics_.add_error("decoration is not valid for functions",
-                             deco->source());
-      return false;
-    }
-  }
-  if (stage_deco_count > 1) {
-    diagnostics_.add_error(
-        "v-0020", "only one stage decoration permitted per entry point",
-        func->source());
-    return false;
-  }
-
   // Use a lambda to validate the entry point decorations for a type.
   // Persistent state is used to track which builtins and locations have already
   // been seen, in order to catch conflicts.