wgsl: Implement invariant attribute

Make sure the other backends ICE on unrecognized attributes.

Add E2E tests, currently skipped for the other backends.

Bug: tint:772
Change-Id: I4e68d111ff79b19ebb6c574058a91debcb746011
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57642
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 3e8f37e..322a523 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -24,6 +24,7 @@
 #include "src/ast/external_texture.h"
 #include "src/ast/fallthrough_statement.h"
 #include "src/ast/if_statement.h"
+#include "src/ast/invariant_decoration.h"
 #include "src/ast/loop_statement.h"
 #include "src/ast/override_decoration.h"
 #include "src/ast/return_statement.h"
@@ -111,6 +112,7 @@
 const char kBuiltinDecoration[] = "builtin";
 const char kGroupDecoration[] = "group";
 const char kInterpolateDecoration[] = "interpolate";
+const char kInvariantDecoration[] = "invariant";
 const char kLocationDecoration[] = "location";
 const char kOverrideDecoration[] = "override";
 const char kSizeDecoration[] = "size";
@@ -3002,6 +3004,10 @@
     });
   }
 
+  if (s == kInvariantDecoration) {
+    return create<ast::InvariantDecoration>(t.source());
+  }
+
   if (s == kBuiltinDecoration) {
     return expect_paren_block("builtin decoration", [&]() -> Result {
       auto builtin = expect_builtin();
diff --git a/src/reader/wgsl/parser_impl_function_header_test.cc b/src/reader/wgsl/parser_impl_function_header_test.cc
index f15699f..b3f38c8 100644
--- a/src/reader/wgsl/parser_impl_function_header_test.cc
+++ b/src/reader/wgsl/parser_impl_function_header_test.cc
@@ -61,6 +61,20 @@
   EXPECT_EQ(loc->value(), 1u);
 }
 
+TEST_F(ParserImplTest, FunctionHeader_InvariantReturnType) {
+  auto p = parser("fn main() -> [[invariant]] f32");
+  auto f = p->function_header();
+  ASSERT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(f.matched);
+  EXPECT_FALSE(f.errored);
+
+  EXPECT_EQ(f->name, "main");
+  EXPECT_EQ(f->params.size(), 0u);
+  EXPECT_TRUE(f->return_type->Is<ast::F32>());
+  ASSERT_EQ(f->return_type_decorations.size(), 1u);
+  EXPECT_TRUE(f->return_type_decorations[0]->Is<ast::InvariantDecoration>());
+}
+
 TEST_F(ParserImplTest, FunctionHeader_DecoratedReturnType_WithArrayStride) {
   auto p = parser("fn main() -> [[location(1), stride(16)]] array<f32, 4>");
   auto f = p->function_header();
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 616a3a4..d4c0ed6 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -3092,6 +3092,11 @@
             return false;
           }
           pre += mod;
+
+        } else if (!deco->IsAnyOf<ast::StructMemberAlignDecoration,
+                                  ast::StructMemberSizeDecoration>()) {
+          TINT_ICE(Writer, diagnostics_)
+              << "unhandled struct member attribute: " << deco->name();
         }
       }
 
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 5f82e30..96ebba6 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -2134,6 +2134,11 @@
           return false;
         }
         out << " [[" << attr << "]]";
+      } else if (!deco->IsAnyOf<ast::StructMemberOffsetDecoration,
+                                ast::StructMemberAlignDecoration,
+                                ast::StructMemberSizeDecoration>()) {
+        TINT_ICE(Writer, diagnostics_)
+            << "unhandled struct member attribute: " << deco->name();
       }
     }
 
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index bb96dd6..38e7d62 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -30,6 +30,7 @@
 #include "src/ast/i32.h"
 #include "src/ast/internal_decoration.h"
 #include "src/ast/interpolate_decoration.h"
+#include "src/ast/invariant_decoration.h"
 #include "src/ast/matrix.h"
 #include "src/ast/module.h"
 #include "src/ast/multisampled_texture.h"
@@ -676,6 +677,8 @@
         out << ", " << interpolate->sampling();
       }
       out << ")";
+    } else if (deco->Is<ast::InvariantDecoration>()) {
+      out << "invariant";
     } else if (auto* override_deco = deco->As<ast::OverrideDecoration>()) {
       out << "override";
       if (override_deco->HasValue()) {
diff --git a/test/shader_io/invariant.wgsl b/test/shader_io/invariant.wgsl
new file mode 100644
index 0000000..66b0649
--- /dev/null
+++ b/test/shader_io/invariant.wgsl
@@ -0,0 +1,4 @@
+[[stage(vertex)]]
+fn main() -> [[builtin(position), invariant]] vec4<f32> {
+  return vec4<f32>();
+}
diff --git a/test/shader_io/invariant.wgsl.expected.hlsl b/test/shader_io/invariant.wgsl.expected.hlsl
new file mode 100644
index 0000000..f0fa6fe
--- /dev/null
+++ b/test/shader_io/invariant.wgsl.expected.hlsl
@@ -0,0 +1,10 @@
+SKIP: FAILED
+
+../../src/writer/hlsl/generator_impl.cc:3056 internal compiler error: unhandled struct member attribute: invariant
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
diff --git a/test/shader_io/invariant.wgsl.expected.msl b/test/shader_io/invariant.wgsl.expected.msl
new file mode 100644
index 0000000..6e1ba46
--- /dev/null
+++ b/test/shader_io/invariant.wgsl.expected.msl
@@ -0,0 +1,10 @@
+SKIP: FAILED
+
+../../src/writer/msl/generator_impl.cc:1990 internal compiler error: unhandled struct member attribute: invariant
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
diff --git a/test/shader_io/invariant.wgsl.expected.spvasm b/test/shader_io/invariant.wgsl.expected.spvasm
new file mode 100644
index 0000000..93feddc
--- /dev/null
+++ b/test/shader_io/invariant.wgsl.expected.spvasm
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+
+[[stage(vertex)]]
+fn main() -> [[builtin(position), invariant]] vec4<f32> {
+  return vec4<f32>();
+}
+
+Failed to generate: unknown decoration
diff --git a/test/shader_io/invariant.wgsl.expected.wgsl b/test/shader_io/invariant.wgsl.expected.wgsl
new file mode 100644
index 0000000..66b0649
--- /dev/null
+++ b/test/shader_io/invariant.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+[[stage(vertex)]]
+fn main() -> [[builtin(position), invariant]] vec4<f32> {
+  return vec4<f32>();
+}
diff --git a/test/shader_io/invariant_struct_member.wgsl b/test/shader_io/invariant_struct_member.wgsl
new file mode 100644
index 0000000..97d813f
--- /dev/null
+++ b/test/shader_io/invariant_struct_member.wgsl
@@ -0,0 +1,8 @@
+struct Out {
+  [[builtin(position), invariant]] pos : vec4<f32>;
+};
+
+[[stage(vertex)]]
+fn main() -> Out {
+  return Out();
+}
diff --git a/test/shader_io/invariant_struct_member.wgsl.expected.hlsl b/test/shader_io/invariant_struct_member.wgsl.expected.hlsl
new file mode 100644
index 0000000..f0fa6fe
--- /dev/null
+++ b/test/shader_io/invariant_struct_member.wgsl.expected.hlsl
@@ -0,0 +1,10 @@
+SKIP: FAILED
+
+../../src/writer/hlsl/generator_impl.cc:3056 internal compiler error: unhandled struct member attribute: invariant
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
diff --git a/test/shader_io/invariant_struct_member.wgsl.expected.msl b/test/shader_io/invariant_struct_member.wgsl.expected.msl
new file mode 100644
index 0000000..6e1ba46
--- /dev/null
+++ b/test/shader_io/invariant_struct_member.wgsl.expected.msl
@@ -0,0 +1,10 @@
+SKIP: FAILED
+
+../../src/writer/msl/generator_impl.cc:1990 internal compiler error: unhandled struct member attribute: invariant
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
+
diff --git a/test/shader_io/invariant_struct_member.wgsl.expected.spvasm b/test/shader_io/invariant_struct_member.wgsl.expected.spvasm
new file mode 100644
index 0000000..08c28fc
--- /dev/null
+++ b/test/shader_io/invariant_struct_member.wgsl.expected.spvasm
@@ -0,0 +1,14 @@
+SKIP: FAILED
+
+
+struct Out {
+  [[builtin(position), invariant]]
+  pos : vec4<f32>;
+};
+
+[[stage(vertex)]]
+fn main() -> Out {
+  return Out();
+}
+
+Failed to generate: unknown decoration
diff --git a/test/shader_io/invariant_struct_member.wgsl.expected.wgsl b/test/shader_io/invariant_struct_member.wgsl.expected.wgsl
new file mode 100644
index 0000000..03d9193
--- /dev/null
+++ b/test/shader_io/invariant_struct_member.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+struct Out {
+  [[builtin(position), invariant]]
+  pos : vec4<f32>;
+};
+
+[[stage(vertex)]]
+fn main() -> Out {
+  return Out();
+}