validation: Require [[location]] for [[interpolate]]

The spec now has this requirement:
https://github.com/gpuweb/gpuweb/pull/2251

Fixed: tint:982
Change-Id: I5ac7fdba336116bbcd0d805ba8b52ddab505df55
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/68921
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index d4641a5..1f41c7a 100644
--- a/src/resolver/decoration_validation_test.cc
+++ b/src/resolver/decoration_validation_test.cc
@@ -276,7 +276,7 @@
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, true},
                     TestParams{DecorationKind::kGroup, false},
-                    TestParams{DecorationKind::kInterpolate, true},
+                    // kInterpolate tested separately (requires [[location]])
                     TestParams{DecorationKind::kInvariant, true},
                     TestParams{DecorationKind::kLocation, true},
                     TestParams{DecorationKind::kOverride, false},
@@ -470,7 +470,7 @@
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, true},
                     TestParams{DecorationKind::kGroup, false},
-                    TestParams{DecorationKind::kInterpolate, true},
+                    // kInterpolate tested separately (requires [[location]])
                     TestParams{DecorationKind::kInvariant, true},
                     TestParams{DecorationKind::kLocation, false},
                     TestParams{DecorationKind::kOverride, false},
@@ -618,7 +618,7 @@
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, true},
                     TestParams{DecorationKind::kGroup, false},
-                    TestParams{DecorationKind::kInterpolate, true},
+                    // kInterpolate tested separately (requires [[location]])
                     // kInvariant tested separately (requires position builtin)
                     TestParams{DecorationKind::kLocation, true},
                     TestParams{DecorationKind::kOverride, false},
@@ -1392,6 +1392,47 @@
             "flat interpolation attribute");
 }
 
+TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
+  Func("main",
+       ast::VariableList{
+           Param("a", ty.vec4<f32>(),
+                 {Builtin(ast::Builtin::kPosition),
+                  Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+                              ast::InterpolationSampling::kNone)})},
+       ty.void_(), {},
+       ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: interpolate attribute must only be used with "
+            "[[location]]");
+}
+
+TEST_F(InterpolateTest, MissingLocationAttribute_ReturnType) {
+  Func("main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+       ast::DecorationList{Stage(ast::PipelineStage::kVertex)},
+       {Builtin(ast::Builtin::kPosition),
+        Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+                    ast::InterpolationSampling::kNone)});
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: interpolate attribute must only be used with "
+            "[[location]]");
+}
+
+TEST_F(InterpolateTest, MissingLocationAttribute_Struct) {
+  Structure(
+      "S", {Member("a", ty.f32(),
+                   {Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+                                ast::InterpolationSampling::kNone)})});
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: interpolate attribute must only be used with "
+            "[[location]]");
+}
+
 }  // namespace
 }  // namespace InterpolateTests
 
diff --git a/src/resolver/resolver_validation.cc b/src/resolver/resolver_validation.cc
index cfe76bd..a9f4013 100644
--- a/src/resolver/resolver_validation.cc
+++ b/src/resolver/resolver_validation.cc
@@ -1158,6 +1158,15 @@
         }
       }
 
+      if (interpolate_attribute) {
+        if (!pipeline_io_attribute ||
+            !pipeline_io_attribute->Is<ast::LocationDecoration>()) {
+          AddError("interpolate attribute must only be used with [[location]]",
+                   interpolate_attribute->source);
+          return false;
+        }
+      }
+
       if (invariant_attribute) {
         bool has_position = false;
         if (pipeline_io_attribute) {
@@ -1927,8 +1936,10 @@
       }
     }
 
+    auto has_location = false;
     auto has_position = false;
     const ast::InvariantDecoration* invariant_attribute = nullptr;
+    const ast::InterpolateDecoration* interpolate_attribute = nullptr;
     for (auto* deco : member->Declaration()->decorations) {
       if (!deco->IsAnyOf<ast::BuiltinDecoration,             //
                          ast::InternalDecoration,            //
@@ -1951,6 +1962,7 @@
       if (auto* invariant = deco->As<ast::InvariantDecoration>()) {
         invariant_attribute = invariant;
       } else if (auto* location = deco->As<ast::LocationDecoration>()) {
+        has_location = true;
         if (!ValidateLocationDecoration(location, member->Type(), locations,
                                         member->Declaration()->source)) {
           return false;
@@ -1964,6 +1976,7 @@
           has_position = true;
         }
       } else if (auto* interpolate = deco->As<ast::InterpolateDecoration>()) {
+        interpolate_attribute = interpolate;
         if (!ValidateInterpolateDecoration(interpolate, member->Type())) {
           return false;
         }
@@ -1976,6 +1989,12 @@
       return false;
     }
 
+    if (interpolate_attribute && !has_location) {
+      AddError("interpolate attribute must only be used with [[location]]",
+               interpolate_attribute->source);
+      return false;
+    }
+
     if (auto* member_struct_type = member->Type()->As<sem::Struct>()) {
       if (auto* member_struct_type_block_decoration =
               ast::GetDecoration<ast::StructBlockDecoration>(