[tint][ir][fuzz] Check for invalid @input_attachment_index

This also adds emitting this attribute in the IR disassembler.

Fixes: 354005988

Change-Id: Ib9e05573c002fd35ffa8af79869f05519b13f5e9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/199194
Reviewed-by: James Price <jrprice@google.com>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index 6e0ee31..52024b3 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -212,6 +212,10 @@
          << StyleLiteral(p.binding) << ")";
 }
 
+void Disassembler::EmitInputAttachmentIndex(uint32_t i) {
+    out_ << StyleAttribute("@input_attachment_index") << "(" << StyleLiteral(i) << ")";
+}
+
 void Disassembler::EmitInterpolation(Interpolation interp) {
     out_ << StyleAttribute("@interpolate") << "(";
     out_ << StyleEnum(interp.type);
@@ -513,6 +517,12 @@
                 out_ << " ";
                 EmitBindingPoint(v->BindingPoint().value());
             }
+
+            if (v->InputAttachmentIndex().has_value()) {
+                out_ << " ";
+                EmitInputAttachmentIndex(v->InputAttachmentIndex().value());
+            }
+
             if (v->Attributes().invariant) {
                 out_ << " " << StyleAttribute("@invariant");
             }
diff --git a/src/tint/lang/core/ir/disassembler.h b/src/tint/lang/core/ir/disassembler.h
index 45fedd1..b96f496 100644
--- a/src/tint/lang/core/ir/disassembler.h
+++ b/src/tint/lang/core/ir/disassembler.h
@@ -232,6 +232,7 @@
     void EmitParamAttributes(const FunctionParam* p);
     void EmitReturnAttributes(const Function* func);
     void EmitBindingPoint(BindingPoint p);
+    void EmitInputAttachmentIndex(uint32_t i);
     void EmitInterpolation(Interpolation interp);
     void EmitInstruction(const Instruction* inst);
     void EmitValueWithType(const Instruction* val);
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index f63a499..cef04d5 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -1148,25 +1148,32 @@
         }
     }
 
-    // Check that resource variables have @group and @binding set
-    auto* type = var->Result(0)->Type();
-    if (type == nullptr) {
+    auto* result_type = var->Result(0)->Type();
+    if (result_type == nullptr) {
+        AddError(var) << "result result_type is undefined";
         return;
     }
 
-    if (auto* mv = type->As<type::MemoryView>()) {
+    if (auto* mv = result_type->As<type::MemoryView>()) {
+        // Check that only resource variables have @group and @binding set
         switch (mv->AddressSpace()) {
             case AddressSpace::kHandle:
             case AddressSpace::kStorage:
             case AddressSpace::kUniform:
                 if (!var->BindingPoint().has_value()) {
                     AddError(var) << "resource variable missing binding points";
-                    return;
                 }
                 break;
             default:
                 break;
         }
+
+        // Check that non-handle variables don't have @input_attachment_index set
+        if (var->InputAttachmentIndex().has_value() &&
+            mv->AddressSpace() != AddressSpace::kHandle) {
+            AddError(var) << "'@input_attachment_index' is not valid for non-handle var";
+            return;
+        }
     }
 }
 
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index b4e6c0b..b2aac77 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -2023,6 +2023,189 @@
 )");
 }
 
+TEST_F(IR_ValidatorTest, Var_Function_UnexpectedInputAttachmentIndex) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var<function, f32>();
+        v->SetInputAttachmentIndex(0);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:41 error: var: '@input_attachment_index' is not valid for non-handle var
+    %2:ptr<function, f32, read_write> = var @input_attachment_index(0)
+                                        ^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%my_func = func():void {
+  $B1: {
+    %2:ptr<function, f32, read_write> = var @input_attachment_index(0)
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Var_Private_UnexpectedInputAttachmentIndex) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var<private_, f32>();
+
+        v->SetInputAttachmentIndex(0);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:40 error: var: '@input_attachment_index' is not valid for non-handle var
+    %2:ptr<private, f32, read_write> = var @input_attachment_index(0)
+                                       ^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%my_func = func():void {
+  $B1: {
+    %2:ptr<private, f32, read_write> = var @input_attachment_index(0)
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Var_PushConstant_UnexpectedInputAttachmentIndex) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var<push_constant, f32>();
+        v->SetInputAttachmentIndex(0);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:40 error: var: '@input_attachment_index' is not valid for non-handle var
+    %2:ptr<push_constant, f32, read> = var @input_attachment_index(0)
+                                       ^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%my_func = func():void {
+  $B1: {
+    %2:ptr<push_constant, f32, read> = var @input_attachment_index(0)
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Var_Storage_UnexpectedInputAttachmentIndex) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var<storage, f32>();
+        v->SetBindingPoint(0, 0);
+        v->SetInputAttachmentIndex(0);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:40 error: var: '@input_attachment_index' is not valid for non-handle var
+    %2:ptr<storage, f32, read_write> = var @binding_point(0, 0) @input_attachment_index(0)
+                                       ^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%my_func = func():void {
+  $B1: {
+    %2:ptr<storage, f32, read_write> = var @binding_point(0, 0) @input_attachment_index(0)
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Var_Uniform_UnexpectedInputAttachmentIndex) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var<uniform, f32>();
+        v->SetBindingPoint(0, 0);
+        v->SetInputAttachmentIndex(0);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:34 error: var: '@input_attachment_index' is not valid for non-handle var
+    %2:ptr<uniform, f32, read> = var @binding_point(0, 0) @input_attachment_index(0)
+                                 ^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%my_func = func():void {
+  $B1: {
+    %2:ptr<uniform, f32, read> = var @binding_point(0, 0) @input_attachment_index(0)
+    ret
+  }
+}
+)");
+}
+
+TEST_F(IR_ValidatorTest, Var_Workgroup_UnexpectedInputAttachmentIndex) {
+    auto* f = b.Function("my_func", ty.void_());
+
+    b.Append(f->Block(), [&] {
+        auto* v = b.Var<workgroup, f32>();
+        v->SetInputAttachmentIndex(0);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:3:42 error: var: '@input_attachment_index' is not valid for non-handle var
+    %2:ptr<workgroup, f32, read_write> = var @input_attachment_index(0)
+                                         ^^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+note: # Disassembly
+%my_func = func():void {
+  $B1: {
+    %2:ptr<workgroup, f32, read_write> = var @input_attachment_index(0)
+    ret
+  }
+}
+)");
+}
+
 TEST_F(IR_ValidatorTest, Var_Init_WrongType) {
     auto* f = b.Function("my_func", ty.void_());
 
@@ -5293,16 +5476,24 @@
     auto* f = b.Function("my_func", ty.void_());
 
     b.Append(f->Block(), [&] {
-        auto* var = b.Var(ty.ptr<function, i32>());
-        var->Result(0)->SetType(nullptr);
-        b.Append(mod.allocators.instructions.Create<ir::Store>(var->Result(0), b.Constant(42_u)));
+        auto* result = b.InstructionResult(ty.u32());
+        result->SetType(nullptr);
+        b.Append(mod.allocators.instructions.Create<ir::Store>(result, b.Constant(42_u)));
         b.Return(f);
     });
 
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:4:11 error: store: store target operand is not a memory view
+              R"(:3:11 error: store: %2 is not in scope
+    store %2, 42u
+          ^^
+
+:2:3 note: in block
+  $B1: {
+  ^^^
+
+:3:11 error: store: store target operand is not a memory view
     store %2, 42u
           ^^
 
@@ -5313,7 +5504,6 @@
 note: # Disassembly
 %my_func = func():void {
   $B1: {
-    %2:null = var
     store %2, 42u
     ret
   }