[wgsl-reader] Parsing sampled texture type

Bug: tint:147
Change-Id: I21c864dd63c2003797c78758358ab4134cd8ff31
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28002
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 6add641..7462bef 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -909,6 +909,7 @@
     "src/reader/wgsl/parser_impl_postfix_expression_test.cc",
     "src/reader/wgsl/parser_impl_primary_expression_test.cc",
     "src/reader/wgsl/parser_impl_relational_expression_test.cc",
+    "src/reader/wgsl/parser_impl_sampled_texture_type_test.cc",
     "src/reader/wgsl/parser_impl_sampler_type_test.cc",
     "src/reader/wgsl/parser_impl_shift_expression_test.cc",
     "src/reader/wgsl/parser_impl_statement_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c735b0d..3d82ef6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -440,6 +440,7 @@
     reader/wgsl/parser_impl_postfix_expression_test.cc
     reader/wgsl/parser_impl_primary_expression_test.cc
     reader/wgsl/parser_impl_relational_expression_test.cc
+    reader/wgsl/parser_impl_sampled_texture_type_test.cc
     reader/wgsl/parser_impl_sampler_type_test.cc
     reader/wgsl/parser_impl_shift_expression_test.cc
     reader/wgsl/parser_impl_statement_test.cc
diff --git a/src/ast/type/texture_type.cc b/src/ast/type/texture_type.cc
index a5a4e1e..690a514 100644
--- a/src/ast/type/texture_type.cc
+++ b/src/ast/type/texture_type.cc
@@ -26,6 +26,9 @@
 
 std::ostream& operator<<(std::ostream& out, TextureDimension dim) {
   switch (dim) {
+    case TextureDimension::kNone:
+      out << "None";
+      break;
     case TextureDimension::k1d:
       out << "1d";
       break;
diff --git a/src/ast/type/texture_type.h b/src/ast/type/texture_type.h
index 37ca7e1..e0f44fa 100644
--- a/src/ast/type/texture_type.h
+++ b/src/ast/type/texture_type.h
@@ -30,6 +30,8 @@
 
 /// The dimensionality of the texture
 enum class TextureDimension {
+  /// Invalid texture
+  kNone = -1,
   /// 1 dimensional texture
   k1d,
   /// 1 dimenstional array texture
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index c2126a8..eacd7ab 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -587,6 +587,32 @@
     return {Token::Type::kTextureDepthCubeArray, source,
             "texture_depth_cube_array"};
   }
+  if (str == "texture_sampled_1d")
+    return {Token::Type::kTextureSampled1d, source, "texture_sampled_1d"};
+  if (str == "texture_sampled_1d_array") {
+    return {Token::Type::kTextureSampled1dArray, source,
+            "texture_sampled_1d_array"};
+  }
+  if (str == "texture_sampled_2d")
+    return {Token::Type::kTextureSampled2d, source, "texture_sampled_2d"};
+  if (str == "texture_sampled_2d_array") {
+    return {Token::Type::kTextureSampled2dArray, source,
+            "texture_sampled_2d_array"};
+  }
+  if (str == "texture_sampled_2d_ms")
+    return {Token::Type::kTextureSampled2dMs, source, "texture_sampled_2d_ms"};
+  if (str == "texture_sampled_2d_ms_array") {
+    return {Token::Type::kTextureSampled2dMsArray, source,
+            "texture_sampled_2d_ms_array"};
+  }
+  if (str == "texture_sampled_3d")
+    return {Token::Type::kTextureSampled3d, source, "texture_sampled_3d"};
+  if (str == "texture_sampled_cube")
+    return {Token::Type::kTextureSampledCube, source, "texture_sampled_cube"};
+  if (str == "texture_sampled_cube_array") {
+    return {Token::Type::kTextureSampledCubeArray, source,
+            "texture_sampled_cube_array"};
+  }
   if (str == "true")
     return {Token::Type::kTrue, source, "true"};
   if (str == "type")
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index a2169cf..0dc5bfa 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -470,6 +470,19 @@
         TokenData{"texture_depth_cube", Token::Type::kTextureDepthCube},
         TokenData{"texture_depth_cube_array",
                   Token::Type::kTextureDepthCubeArray},
+        TokenData{"texture_sampled_1d", Token::Type::kTextureSampled1d},
+        TokenData{"texture_sampled_1d_array",
+                  Token::Type::kTextureSampled1dArray},
+        TokenData{"texture_sampled_2d", Token::Type::kTextureSampled2d},
+        TokenData{"texture_sampled_2d_array",
+                  Token::Type::kTextureSampled2dArray},
+        TokenData{"texture_sampled_2d_ms", Token::Type::kTextureSampled2dMs},
+        TokenData{"texture_sampled_2d_ms_array",
+                  Token::Type::kTextureSampled2dMsArray},
+        TokenData{"texture_sampled_3d", Token::Type::kTextureSampled3d},
+        TokenData{"texture_sampled_cube", Token::Type::kTextureSampledCube},
+        TokenData{"texture_sampled_cube_array",
+                  Token::Type::kTextureSampledCubeArray},
         TokenData{"true", Token::Type::kTrue},
         TokenData{"type", Token::Type::kType},
         TokenData{"u32", Token::Type::kU32},
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index e7a748b..4316930 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -51,6 +51,7 @@
 #include "src/ast/type/i32_type.h"
 #include "src/ast/type/matrix_type.h"
 #include "src/ast/type/pointer_type.h"
+#include "src/ast/type/sampled_texture_type.h"
 #include "src/ast/type/sampler_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/u32_type.h"
@@ -580,7 +581,7 @@
 // texture_sampler_types
 //  : sampler_type
 //  | depth_texture_type
-//  | TODO: sampled_texture_type LESS_THAN type_decl GREATER_THAN
+//  | sampled_texture_type LESS_THAN type_decl GREATER_THAN
 //  | TODO: multisampled_texture_type LESS_THAN type_decl GREATER_THAN
 //  | TODO: storage_texture_type LESS_THAN image_storage_type GREATER_THAN
 ast::type::Type* ParserImpl::texture_sampler_types() {
@@ -594,6 +595,32 @@
     return type;
   }
 
+  auto dim = sampled_texture_type();
+  if (dim != ast::type::TextureDimension::kNone) {
+    auto t = next();
+    if (!t.IsLessThan()) {
+      set_error(peek(), "missing '<' for sampled texture type");
+      return nullptr;
+    }
+
+    auto* subtype = type_decl();
+    if (has_error())
+      return nullptr;
+    if (subtype == nullptr) {
+      set_error(peek(), "invalid subtype for sampled texture type");
+      return nullptr;
+    }
+
+    t = next();
+    if (!t.IsGreaterThan()) {
+      set_error(peek(), "missing '>' for sampled texture type");
+      return nullptr;
+    }
+
+    return ctx_.type_mgr().Get(
+        std::make_unique<ast::type::SampledTextureType>(dim, subtype));
+  }
+
   return nullptr;
 }
 
@@ -615,6 +642,57 @@
   return nullptr;
 }
 
+// sampled_texture_type
+//  : TEXTURE_SAMPLED_1D
+//  | TEXTURE_SAMPLED_1D_ARRAY
+//  | TEXTURE_SAMPLED_2D
+//  | TEXTURE_SAMPLED_2D_ARRAY
+//  | TEXTURE_SAMPLED_2D_MS
+//  | TEXTURE_SAMPLED_2D_MS_ARRAY
+//  | TEXTURE_SAMPLED_3D
+//  | TEXTURE_SAMPLED_CUBE
+//  | TEXTURE_SAMPLED_CUBE_ARRAY
+ast::type::TextureDimension ParserImpl::sampled_texture_type() {
+  auto t = peek();
+  if (t.IsTextureSampled1d()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k1d;
+  }
+  if (t.IsTextureSampled1dArray()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k1dArray;
+  }
+  if (t.IsTextureSampled2d()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k2d;
+  }
+  if (t.IsTextureSampled2dArray()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k2dArray;
+  }
+  if (t.IsTextureSampled2dMs()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k2dMs;
+  }
+  if (t.IsTextureSampled2dMsArray()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k2dMsArray;
+  }
+  if (t.IsTextureSampled3d()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::k3d;
+  }
+  if (t.IsTextureSampledCube()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::kCube;
+  }
+  if (t.IsTextureSampledCubeArray()) {
+    next();  // Consume the peek
+    return ast::type::TextureDimension::kCubeArray;
+  }
+  return ast::type::TextureDimension::kNone;
+}
+
 // depth_texture_type
 //  : TEXTURE_DEPTH_2D
 //  | TEXTURE_DEPTH_2D_ARRAY
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index d492438..3f3073f 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -40,6 +40,7 @@
 #include "src/ast/struct_decoration.h"
 #include "src/ast/struct_member.h"
 #include "src/ast/struct_member_decoration.h"
+#include "src/ast/type/texture_type.h"
 #include "src/ast/type/type.h"
 #include "src/ast/variable.h"
 #include "src/ast/variable_decoration.h"
@@ -186,6 +187,9 @@
   /// Parses a `sampler_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
   ast::type::Type* sampler_type();
+  /// Parses a `sampled_texture_type` grammar element
+  /// @returns returns the sample texture dimension or kNone if none matched.
+  ast::type::TextureDimension sampled_texture_type();
   /// Parses a `depth_texture_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
   ast::type::Type* depth_texture_type();
diff --git a/src/reader/wgsl/parser_impl_sampled_texture_type_test.cc b/src/reader/wgsl/parser_impl_sampled_texture_type_test.cc
new file mode 100644
index 0000000..da9a461
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_sampled_texture_type_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/ast/type/texture_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+namespace {
+
+TEST_F(ParserImplTest, SampledTextureType_Invalid) {
+  auto* p = parser("1234");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::kNone);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_1d) {
+  auto* p = parser("texture_sampled_1d");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k1d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_1dArray) {
+  auto* p = parser("texture_sampled_1d_array");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k1dArray);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_2d) {
+  auto* p = parser("texture_sampled_2d");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k2d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_2dArray) {
+  auto* p = parser("texture_sampled_2d_array");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k2dArray);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_2dMs) {
+  auto* p = parser("texture_sampled_2d_ms");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k2dMs);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_2dMsArray) {
+  auto* p = parser("texture_sampled_2d_ms_array");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k2dMsArray);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_3d) {
+  auto* p = parser("texture_sampled_3d");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::k3d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_Cube) {
+  auto* p = parser("texture_sampled_cube");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::kCube);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, SampledTextureType_kCubeArray) {
+  auto* p = parser("texture_sampled_cube_array");
+  auto t = p->sampled_texture_type();
+  EXPECT_EQ(t, ast::type::TextureDimension::kCubeArray);
+  EXPECT_FALSE(p->has_error());
+}
+
+}  // namespace
+}  // namespace wgsl
+}  // namespace reader
+}  // namespace tint
diff --git a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
index a1580e6..262dca9 100644
--- a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
+++ b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
@@ -39,6 +39,15 @@
   EXPECT_FALSE(p->has_error());
 }
 
+TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
+  auto* p = parser("sampler_comparison");
+  auto* t = p->texture_sampler_types();
+  ASSERT_NE(t, nullptr);
+  ASSERT_TRUE(t->IsSampler());
+  ASSERT_TRUE(t->AsSampler()->IsComparison());
+  EXPECT_FALSE(p->has_error());
+}
+
 TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
   auto* p = parser("texture_depth_2d");
   auto* t = p->texture_sampler_types();
@@ -49,6 +58,67 @@
   EXPECT_FALSE(p->has_error());
 }
 
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
+  auto* p = parser("texture_sampled_1d<f32>");
+  auto* t = p->texture_sampler_types();
+  ASSERT_NE(t, nullptr);
+  ASSERT_TRUE(t->IsTexture());
+  ASSERT_TRUE(t->AsTexture()->IsSampled());
+  ASSERT_TRUE(t->AsTexture()->AsSampled()->type()->IsF32());
+  EXPECT_EQ(t->AsTexture()->dim(), ast::type::TextureDimension::k1d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
+  auto* p = parser("texture_sampled_2d<i32>");
+  auto* t = p->texture_sampler_types();
+  ASSERT_NE(t, nullptr);
+  ASSERT_TRUE(t->IsTexture());
+  ASSERT_TRUE(t->AsTexture()->IsSampled());
+  ASSERT_TRUE(t->AsTexture()->AsSampled()->type()->IsI32());
+  EXPECT_EQ(t->AsTexture()->dim(), ast::type::TextureDimension::k2d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
+  auto* p = parser("texture_sampled_3d<u32>");
+  auto* t = p->texture_sampler_types();
+  ASSERT_NE(t, nullptr);
+  ASSERT_TRUE(t->IsTexture());
+  ASSERT_TRUE(t->AsTexture()->IsSampled());
+  ASSERT_TRUE(t->AsTexture()->AsSampled()->type()->IsU32());
+  EXPECT_EQ(t->AsTexture()->dim(), ast::type::TextureDimension::k3d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_Invalid) {
+  auto* p = parser("texture_sampled_1d<abc>");
+  auto* t = p->texture_sampler_types();
+  EXPECT_EQ(t, nullptr);
+  EXPECT_EQ(p->error(), "1:20: unknown type alias 'abc'");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingType) {
+  auto* p = parser("texture_sampled_1d<>");
+  auto* t = p->texture_sampler_types();
+  EXPECT_EQ(t, nullptr);
+  EXPECT_EQ(p->error(), "1:20: invalid subtype for sampled texture type");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan) {
+  auto* p = parser("texture_sampled_1d");
+  auto* t = p->texture_sampler_types();
+  EXPECT_EQ(t, nullptr);
+  EXPECT_EQ(p->error(), "1:19: missing '<' for sampled texture type");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingGreaterThan) {
+  auto* p = parser("texture_sampled_1d<u32");
+  auto* t = p->texture_sampler_types();
+  EXPECT_EQ(t, nullptr);
+  EXPECT_EQ(p->error(), "1:23: missing '>' for sampled texture type");
+}
+
 }  // namespace
 }  // namespace wgsl
 }  // namespace reader
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index 9ca01c9..be6f752 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -219,6 +219,24 @@
       return "texture_depth_cube";
     case Token::Type::kTextureDepthCubeArray:
       return "texture_depth_cube_array";
+    case Token::Type::kTextureSampled1d:
+      return "texture_sampled_1d";
+    case Token::Type::kTextureSampled1dArray:
+      return "texture_sampled_1d_array";
+    case Token::Type::kTextureSampled2d:
+      return "texture_sampled_2d";
+    case Token::Type::kTextureSampled2dArray:
+      return "texture_sampled_2d_array";
+    case Token::Type::kTextureSampled2dMs:
+      return "texture_sampled_2d_ms";
+    case Token::Type::kTextureSampled2dMsArray:
+      return "texture_sampled_2d_ms_array";
+    case Token::Type::kTextureSampled3d:
+      return "texture_sampled_3d";
+    case Token::Type::kTextureSampledCube:
+      return "texture_sampled_cube";
+    case Token::Type::kTextureSampledCubeArray:
+      return "texture_sampled_cube_array";
     case Token::Type::kTrue:
       return "true";
     case Token::Type::kType:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index 3740b79..1b8d5f7 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -230,6 +230,24 @@
     kTextureDepthCube,
     /// A 'texture_depth_cube_array'
     kTextureDepthCubeArray,
+    /// A 'texture_sampled_1d'
+    kTextureSampled1d,
+    /// A 'texture_sampled_1d_array'
+    kTextureSampled1dArray,
+    /// A 'texture_sampled_2d'
+    kTextureSampled2d,
+    /// A 'texture_sampled_2d_array'
+    kTextureSampled2dArray,
+    /// A 'texture_sampled_2d_ms'
+    kTextureSampled2dMs,
+    /// A 'texture_sampled_2d_ms_array'
+    kTextureSampled2dMsArray,
+    /// A 'texture_sampled_3d'
+    kTextureSampled3d,
+    /// A 'texture_sampled_cube'
+    kTextureSampledCube,
+    /// A 'texture_sampled_cube_array'
+    kTextureSampledCubeArray,
     /// A 'true'
     kTrue,
     /// A 'type'
@@ -503,6 +521,36 @@
   bool IsTextureDepthCubeArray() const {
     return type_ == Type::kTextureDepthCubeArray;
   }
+  /// @returns true if token is a 'texture_sampled_1d'
+  bool IsTextureSampled1d() const { return type_ == Type::kTextureSampled1d; }
+  /// @returns true if token is a 'texture_sampled_1d_array'
+  bool IsTextureSampled1dArray() const {
+    return type_ == Type::kTextureSampled1dArray;
+  }
+  /// @returns true if token is a 'texture_sampled_2d'
+  bool IsTextureSampled2d() const { return type_ == Type::kTextureSampled2d; }
+  /// @returns true if token is a 'texture_sampled_2d_array'
+  bool IsTextureSampled2dArray() const {
+    return type_ == Type::kTextureSampled2dArray;
+  }
+  /// @returns true if token is a 'texture_sampled_2d_ms'
+  bool IsTextureSampled2dMs() const {
+    return type_ == Type::kTextureSampled2dMs;
+  }
+  /// @returns true if token is a 'texture_sampled_2d_ms_array'
+  bool IsTextureSampled2dMsArray() const {
+    return type_ == Type::kTextureSampled2dMsArray;
+  }
+  /// @returns true if token is a 'texture_sampled_3d'
+  bool IsTextureSampled3d() const { return type_ == Type::kTextureSampled3d; }
+  /// @returns true if token is a 'texture_sampled_cube'
+  bool IsTextureSampledCube() const {
+    return type_ == Type::kTextureSampledCube;
+  }
+  /// @returns true if token is a 'texture_sampled_cube_array'
+  bool IsTextureSampledCubeArray() const {
+    return type_ == Type::kTextureSampledCubeArray;
+  }
   /// @returns true if token is a 'true'
   bool IsTrue() const { return type_ == Type::kTrue; }
   /// @returns true if token is a 'type'