[Tint] Validate subgroup builtins as input only

Validate that subgroup_size and subgroup_invocation_id are input only.

Bug: 352540632
Change-Id: Iaeab5781f9022cf085177664711ab0a2a93fd5ac
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/198574
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Natalie Chouinard <chouinard@google.com>
diff --git a/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc b/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc
index 5e6a8b1..c93edfc 100644
--- a/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc
+++ b/src/tint/lang/wgsl/resolver/subgroups_extension_test.cc
@@ -30,6 +30,7 @@
 
 #include "gmock/gmock.h"
 
+using namespace tint::core::fluent_types;     // NOLINT
 using namespace tint::core::number_suffixes;  // NOLINT
 
 namespace tint::resolver {
@@ -165,6 +166,47 @@
               "error: '@builtin(subgroup_size)' is only valid as a compute shader input");
 }
 
+TEST_F(ResolverSubgroupsExtensionTest, SubgroupSizeComputeShaderOutput) {
+    Enable(wgsl::Extension::kSubgroups);
+
+    Func("main", tint::Empty, ty.u32(),
+         Vector{
+             Return(Call<u32>()),
+         },
+         Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         },
+         Vector{Builtin(Source{{1, 2}}, core::BuiltinValue::kSubgroupSize)});
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "1:2 error: '@builtin(subgroup_size)' is only valid as a compute shader input");
+}
+
+TEST_F(ResolverSubgroupsExtensionTest, SubgroupSizeComputeShaderStructOutput) {
+    Enable(wgsl::Extension::kSubgroups);
+
+    auto* s = Structure(
+        "Output", Vector{
+                      Member("a", ty.u32(), Vector{Builtin(core::BuiltinValue::kSubgroupSize)}),
+                  });
+
+    Func("main", tint::Empty, ty.Of(s),
+         Vector{
+             Return(Call(ty.Of(s), Call<u32>())),
+         },
+         Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         });
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: '@builtin(subgroup_size)' is only valid as a compute shader input
+note: while analyzing entry point 'main')");
+}
+
 // Using builtin(subgroup_invocation_id) for anything other than a compute shader input should fail.
 TEST_F(ResolverSubgroupsExtensionTest, SubgroupInvocationIdFragmentShader) {
     Enable(wgsl::Extension::kSubgroups);
@@ -177,5 +219,24 @@
               "error: '@builtin(subgroup_invocation_id)' is only valid as a compute shader input");
 }
 
+TEST_F(ResolverSubgroupsExtensionTest, SubgroupInvocationIdComputeShaderOutput) {
+    Enable(wgsl::Extension::kSubgroups);
+
+    Func("main", tint::Empty, ty.u32(),
+         Vector{
+             Return(Call<u32>()),
+         },
+         Vector{
+             Stage(ast::PipelineStage::kCompute),
+             WorkgroupSize(1_i),
+         },
+         Vector{Builtin(Source{{1, 2}}, core::BuiltinValue::kSubgroupInvocationId)});
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        "1:2 error: '@builtin(subgroup_invocation_id)' is only valid as a compute shader input");
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index 964ebea..32d158d 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -1109,7 +1109,8 @@
                 err_builtin_type("u32");
                 return false;
             }
-            if (stage != ast::PipelineStage::kNone && stage != ast::PipelineStage::kCompute) {
+            if (stage != ast::PipelineStage::kNone &&
+                !(stage == ast::PipelineStage::kCompute && is_input)) {
                 AddError(attr->source)
                     << style::Attribute("@builtin") << style::Code("(", style::Enum(builtin), ")")
                     << " is only valid as a compute shader input";