IR Validator: Reject builtin params on non-entry-point functions

This patch rejects any builtins on a function parameter that is
not an entry point in Tint IR validator.

Fixed: 420012858, 419743047
Bug: 348701956
Test: tint_unittests
Change-Id: I75c32c2613b6218a17c1506c419c50c6400615eb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/243716
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index ffc29a7..24fc0db 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -2193,6 +2193,9 @@
                 AddError(param)
                     << "input param to non-entry point function has a binding point set";
             }
+            if (param->Builtin().has_value()) {
+                AddError(param) << "builtins can only be decorated on entry point params";
+            }
         }
 
         if (!capabilities_.Contains(Capability::kAllowWorkspacePointerInputToEntryPoint) &&
diff --git a/src/tint/lang/core/ir/validator_builtin_test.cc b/src/tint/lang/core/ir/validator_builtin_test.cc
index 79e034e..0aada68 100644
--- a/src/tint/lang/core/ir/validator_builtin_test.cc
+++ b/src/tint/lang/core/ir/validator_builtin_test.cc
@@ -1022,6 +1022,19 @@
 )")) << res.Failure();
 }
 
+TEST_F(IR_ValidatorTest, Builtin_NoStage) {
+    auto* f = b.Function("f", ty.void_());
+    AddBuiltinParam(f, "id", BuiltinValue::kLocalInvocationId, ty.vec3<u32>());
+
+    b.Append(f->Block(), [&] { b.Unreachable(); });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(res.Failure().reason, testing::HasSubstr(
+                                          R"(builtins can only be decorated on entry point params
+)")) << res.Failure();
+}
+
 namespace {
 template <typename T>
 static const core::type::Type* TypeBuilder(core::type::Manager& m) {