[wgsl-reader][wgsl-writer] Update array decorations to new syntax.

This CL updates the array decorations to the new syntax requiring ()'s
around parameters.

Bug: tint:238
Change-Id: I5a8b19e302fbd3f78392553d8d1c5e882b900180
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28601
Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 5781a79..3b9d7a8 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -1334,7 +1334,7 @@
 // array_decoration_list
 //   : ATTR_LEFT (array_decoration COMMA)* array_decoration ATTR_RIGHT
 // array_decoration
-//   : STRIDE INT_LITERAL
+//   : STRIDE PAREN_LEFT INT_LITERAL PAREN_RIGHT
 //
 // As there is currently only one decoration I'm combining these for now.
 // we can split apart later if needed.
@@ -1352,6 +1352,12 @@
   next();  // consume the peek of stride
 
   t = next();
+  if (!t.IsParenLeft()) {
+    set_error(t, "missing ( for stride attribute");
+    return 0;
+  }
+
+  t = next();
   if (!t.IsSintLiteral()) {
     set_error(t, "missing value for stride decoration");
     return 0;
@@ -1360,8 +1366,14 @@
     set_error(t, "invalid stride value: " + t.to_str());
     return 0;
   }
-
   uint32_t stride = static_cast<uint32_t>(t.to_i32());
+
+  t = next();
+  if (!t.IsParenRight()) {
+    set_error(t, "missing ) for stride attribute");
+    return 0;
+  }
+
   t = next();
   if (!t.IsAttrRight()) {
     set_error(t, "missing ]] for array decoration");
diff --git a/src/reader/wgsl/parser_impl_type_alias_test.cc b/src/reader/wgsl/parser_impl_type_alias_test.cc
index d60360b..f90ebac 100644
--- a/src/reader/wgsl/parser_impl_type_alias_test.cc
+++ b/src/reader/wgsl/parser_impl_type_alias_test.cc
@@ -91,7 +91,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Struct_WithStride) {
   auto* p = parser(
-      "type a = [[block]] struct { [[offset(0)]] data: [[stride 4]] "
+      "type a = [[block]] struct { [[offset(0)]] data: [[stride(4)]] "
       "array<f32>; "
       "}");
   auto* t = p->type_alias();
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
index bb9a616..b4c96aa 100644
--- a/src/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -395,9 +395,9 @@
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
-  auto* p = parser("[[stride 16]] array<f32, 5>");
+  auto* p = parser("[[stride(16)]] array<f32, 5>");
   auto* t = p->type_decl();
-  ASSERT_NE(t, nullptr);
+  ASSERT_NE(t, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
   ASSERT_TRUE(t->IsArray());
 
@@ -410,9 +410,9 @@
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Stride) {
-  auto* p = parser("[[stride 16]] array<f32>");
+  auto* p = parser("[[stride(16)]] array<f32>");
   auto* t = p->type_decl();
-  ASSERT_NE(t, nullptr);
+  ASSERT_NE(t, nullptr) << p->error();
   ASSERT_FALSE(p->has_error());
   ASSERT_TRUE(t->IsArray());
 
@@ -424,19 +424,19 @@
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingArray) {
-  auto* p = parser("[[stride 16]] f32");
+  auto* p = parser("[[stride(16)]] f32");
   auto* t = p->type_decl();
   ASSERT_EQ(t, nullptr);
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:15: found array decoration but no array");
+  EXPECT_EQ(p->error(), "1:16: found array decoration but no array");
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Decoration_MissingClosingAttr) {
-  auto* p = parser("[[stride 16 array<f32, 5>");
+  auto* p = parser("[[stride(16) array<f32, 5>");
   auto* t = p->type_decl();
   ASSERT_EQ(t, nullptr);
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:13: missing ]] for array decoration");
+  EXPECT_EQ(p->error(), "1:14: missing ]] for array decoration");
 }
 
 // Note, this isn't an error because it could be a struct decoration, we just
@@ -448,16 +448,32 @@
   ASSERT_FALSE(p->has_error());
 }
 
-TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingValue) {
-  auto* p = parser("[[stride]] array<f32, 5>");
+TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingLeftParen) {
+  auto* p = parser("[[stride 4)]] array<f32, 5>");
   auto* t = p->type_decl();
   ASSERT_EQ(t, nullptr);
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:9: missing value for stride decoration");
+  EXPECT_EQ(p->error(), "1:10: missing ( for stride attribute");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingRightParen) {
+  auto* p = parser("[[stride(4]] array<f32, 5>");
+  auto* t = p->type_decl();
+  ASSERT_EQ(t, nullptr);
+  ASSERT_TRUE(p->has_error());
+  EXPECT_EQ(p->error(), "1:11: missing ) for stride attribute");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_Stride_MissingValue) {
+  auto* p = parser("[[stride()]] array<f32, 5>");
+  auto* t = p->type_decl();
+  ASSERT_EQ(t, nullptr);
+  ASSERT_TRUE(p->has_error());
+  EXPECT_EQ(p->error(), "1:10: missing value for stride decoration");
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_InvalidValue) {
-  auto* p = parser("[[stride invalid]] array<f32, 5>");
+  auto* p = parser("[[stride(invalid)]] array<f32, 5>");
   auto* t = p->type_decl();
   ASSERT_EQ(t, nullptr);
   ASSERT_TRUE(p->has_error());
@@ -465,7 +481,7 @@
 }
 
 TEST_F(ParserImplTest, TypeDecl_Array_Stride_InvalidValue_Negative) {
-  auto* p = parser("[[stride -1]] array<f32, 5>");
+  auto* p = parser("[[stride(-1)]] array<f32, 5>");
   auto* t = p->type_decl();
   ASSERT_EQ(t, nullptr);
   ASSERT_TRUE(p->has_error());
diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc
index 8f75a4a..040804c 100644
--- a/src/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -516,7 +516,7 @@
 TEST_F(HlslGeneratorImplTest_MemberAccessor,
        EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray) {
   // struct Data {
-  //   [[offset(0)]] a : [[stride 4]] array<i32, 5>;
+  //   [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
   // };
   // var<storage_buffer> data : Data;
   // data.a[2];
@@ -564,7 +564,7 @@
 TEST_F(HlslGeneratorImplTest_MemberAccessor,
        EmitExpression_ArrayAccessor_StorageBuffer_Load_Int_FromArray_ExprIdx) {
   // struct Data {
-  //   [[offset(0)]] a : [[stride 4]] array<i32, 5>;
+  //   [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
   // };
   // var<storage_buffer> data : Data;
   // data.a[(2 + 4) - 3];
@@ -674,7 +674,7 @@
 TEST_F(HlslGeneratorImplTest_MemberAccessor,
        EmitExpression_MemberAccessor_StorageBuffer_Store_ToArray) {
   // struct Data {
-  //   [[offset(0)]] a : [[stride 4]] array<i32, 5>;
+  //   [[offset(0)]] a : [[stride(4)]] array<i32, 5>;
   // };
   // var<storage_buffer> data : Data;
   // data.a[2] = 2;
@@ -907,7 +907,7 @@
   //   [[offset(16)]] b : vec3<f32>;
   // };
   // struct Pre {
-  //   var c : [[stride 32]] array<Data, 4>;
+  //   var c : [[stride(32)]] array<Data, 4>;
   // };
   //
   // var<storage_buffer> data : Pre;
@@ -980,7 +980,7 @@
   //   [[offset(16)]] b : vec3<f32>;
   // };
   // struct Pre {
-  //   var c : [[stride 32]] array<Data, 4>;
+  //   var c : [[stride(32)]] array<Data, 4>;
   // };
   //
   // var<storage_buffer> data : Pre;
@@ -1056,7 +1056,7 @@
   //   [[offset(16)]] b : vec3<f32>;
   // };
   // struct Pre {
-  //   var c : [[stride 32]] array<Data, 4>;
+  //   var c : [[stride(32)]] array<Data, 4>;
   // };
   //
   // var<storage_buffer> data : Pre;
@@ -1131,7 +1131,7 @@
   //   [[offset(16)]] b : vec3<f32>;
   // };
   // struct Pre {
-  //   var c : [[stride 32]] array<Data, 4>;
+  //   var c : [[stride(32)]] array<Data, 4>;
   // };
   //
   // var<storage_buffer> data : Pre;
@@ -1207,7 +1207,7 @@
   //   [[offset(16)]] b : vec3<f32>;
   // };
   // struct Pre {
-  //   var c : [[stride 32]] array<Data, 4>;
+  //   var c : [[stride(32)]] array<Data, 4>;
   // };
   //
   // var<storage_buffer> data : Pre;
@@ -1299,7 +1299,7 @@
   //   [[offset(16)]] b : vec3<f32>;
   // };
   // struct Pre {
-  //   var c : [[stride 32]] array<Data, 4>;
+  //   var c : [[stride(32)]] array<Data, 4>;
   // };
   //
   // var<storage_buffer> data : Pre;
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index d13d1bb..7880442 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -572,7 +572,7 @@
     auto* ary = type->AsArray();
 
     if (ary->has_array_stride()) {
-      out_ << "[[stride " << ary->array_stride() << "]] ";
+      out_ << "[[stride(" << ary->array_stride() << ")]] ";
     }
 
     out_ << "array<";
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc
index 05aade7..c6e1352 100644
--- a/src/writer/wgsl/generator_impl_type_test.cc
+++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -67,7 +67,7 @@
 
   GeneratorImpl g;
   ASSERT_TRUE(g.EmitType(&a)) << g.error();
-  EXPECT_EQ(g.result(), "[[stride 16]] array<bool, 4>");
+  EXPECT_EQ(g.result(), "[[stride(16)]] array<bool, 4>");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_RuntimeArray) {
diff --git a/test/compute_boids.wgsl b/test/compute_boids.wgsl
index 1cf7d3a..e567f74 100644
--- a/test/compute_boids.wgsl
+++ b/test/compute_boids.wgsl
@@ -57,7 +57,7 @@
 };
 
 type Particles = [[block]] struct {
-  [[offset(0)]] particles : [[stride 16]] array<Particle, 5>;
+  [[offset(0)]] particles : [[stride(16)]] array<Particle, 5>;
 };
 
 [[binding 0, set 0]] var<uniform> params : SimParams;