Add texture_external parsing and intrinsic overloads

Adds texture_external to ParserImpl. Adds texture_external overloads to
TextureSample, TextureLoad, and TextureDimensions to the intrisic table.
Adds corresponding tests.

Bug: dawn:728
Change-Id: I5e5557a10327f8c3d3044319decd748f812ecf3e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48722
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d5e3737..605fa55 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -639,6 +639,7 @@
       reader/wgsl/parser_impl_continue_stmt_test.cc
       reader/wgsl/parser_impl_continuing_stmt_test.cc
       reader/wgsl/parser_impl_depth_texture_type_test.cc
+      reader/wgsl/parser_impl_external_texture_type_test.cc
       reader/wgsl/parser_impl_else_stmt_test.cc
       reader/wgsl/parser_impl_elseif_stmt_test.cc
       reader/wgsl/parser_impl_equality_expression_test.cc
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index b4164ed..2652ab8 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -22,6 +22,7 @@
 #include "src/program_builder.h"
 #include "src/sem/access_control_type.h"
 #include "src/sem/depth_texture_type.h"
+#include "src/sem/external_texture_type.h"
 #include "src/sem/multisampled_texture_type.h"
 #include "src/sem/sampled_texture_type.h"
 #include "src/sem/storage_texture_type.h"
@@ -570,6 +571,22 @@
   OpenType const channel_format_;
 };
 
+/// ExternalTextureBuilder is a Matcher / Builder for external textures.
+class ExternalTextureBuilder : public Builder {
+ public:
+  ExternalTextureBuilder() {}
+
+  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+    return ty->Is<sem::ExternalTexture>();
+  }
+
+  sem::Type* Build(BuildState& state) const override {
+    return state.ty_mgr.Get<sem::ExternalTexture>();
+  }
+
+  std::string str() const override { return "texture_external"; }
+};
+
 /// SamplerBuilder is a Matcher / Builder for sampler types of the given kind.
 class SamplerBuilder : public Builder {
  public:
@@ -759,6 +776,11 @@
         dimensions, texel_format, channel_format);
   }
 
+  /// @returns a Matcher / Builder that matches an external texture
+  Builder* external_texture() {
+    return matcher_allocator_.Create<ExternalTextureBuilder>();
+  }
+
   /// @returns a Matcher / Builder that matches a sampler type
   Builder* sampler(ast::SamplerKind kind) {
     return matcher_allocator_.Create<SamplerBuilder>(kind);
@@ -1091,6 +1113,7 @@
   auto* tex_depth_2d_array = depth_texture(Dim::k2dArray);
   auto* tex_depth_cube = depth_texture(Dim::kCube);
   auto* tex_depth_cube_array = depth_texture(Dim::kCubeArray);
+  auto* tex_external = external_texture();
   auto* tex_storage_1d_FT =
       storage_texture(Dim::k1d, OpenNumber::F, OpenType::T);
   auto* tex_storage_2d_FT =
@@ -1159,6 +1182,7 @@
   Register(I::kTextureDimensions, vec2_i32, {{t, tex_storage_2d_FT},                     }); // NOLINT
   Register(I::kTextureDimensions, vec2_i32, {{t, tex_storage_2d_array_FT},               }); // NOLINT
   Register(I::kTextureDimensions, vec3_i32, {{t, tex_storage_3d_FT},                     }); // NOLINT
+  Register(I::kTextureDimensions, vec2_i32, {{t, tex_external},                          }); // NOLINT
 
   Register(I::kTextureNumLayers,  i32, {{t, tex_2d_array_T},          });
   Register(I::kTextureNumLayers,  i32, {{t, tex_cube_array_T},        });
@@ -1195,6 +1219,8 @@
   Register(I::kTextureSample, f32,      {{t, tex_depth_2d_array},   {s, sampler}, {coords, vec2_f32}, {array_index, i32}, {offset, vec2_i32}, }); // NOLINT
   Register(I::kTextureSample, f32,      {{t, tex_depth_cube},       {s, sampler}, {coords, vec3_f32},                                         }); // NOLINT
   Register(I::kTextureSample, f32,      {{t, tex_depth_cube_array}, {s, sampler}, {coords, vec3_f32}, {array_index, i32},                     }); // NOLINT
+  Register(I::kTextureSample, vec4_f32, {{t, tex_external},         {s, sampler}, {coords, vec2_f32},                                         }); // NOLINT
+  Register(I::kTextureSample, vec4_f32, {{t, tex_external},         {s, sampler}, {coords, vec2_f32},                     {offset, vec2_i32}, }); // NOLINT
 
   Register(I::kTextureSampleBias, vec4_f32,    {{t, tex_2d_f32},           {s, sampler}, {coords, vec2_f32},                     {bias, f32},                     }); // NOLINT
   Register(I::kTextureSampleBias, vec4_f32,    {{t, tex_2d_f32},           {s, sampler}, {coords, vec2_f32},                     {bias, f32}, {offset, vec2_i32}, }); // NOLINT
@@ -1241,18 +1267,19 @@
   Register(I::kTextureStore, void_, {{t, tex_storage_wo_2d_array_FT},{coords, vec2_i32}, {array_index, i32}, {value, vec4_T}, }); // NOLINT
   Register(I::kTextureStore, void_, {{t, tex_storage_wo_3d_FT},      {coords, vec3_i32},                     {value, vec4_T}, }); // NOLINT
 
-  Register(I::kTextureLoad, vec4_T, {{t, tex_1d_T},               {coords, i32},                           {level, i32},                      }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_2d_T},               {coords, vec2_i32},                      {level, i32},                      }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_2d_array_T},         {coords, vec2_i32}, {array_index, i32},  {level, i32},                      }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_3d_T},               {coords, vec3_i32},                      {level, i32},                      }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_ms_2d_T},            {coords, vec2_i32},                                    {sample_index, i32}, }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_ms_2d_array_T},      {coords, vec2_i32}, {array_index, i32},                {sample_index, i32}, }); // NOLINT
-  Register(I::kTextureLoad, f32,    {{t, tex_depth_2d},           {coords, vec2_i32},                      {level, i32},                      }); // NOLINT
-  Register(I::kTextureLoad, f32,    {{t, tex_depth_2d_array},     {coords, vec2_i32}, {array_index, i32},  {level, i32},                      }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_1d_FT},      {coords, i32},                                                              }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_2d_FT},      {coords, vec2_i32},                                                         }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_2d_array_FT},{coords, vec2_i32}, {array_index, i32},                                     }); // NOLINT
-  Register(I::kTextureLoad, vec4_T, {{t, tex_storage_ro_3d_FT},      {coords, vec3_i32},                                                         }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_1d_T},                  {coords, i32},                           {level, i32},                      }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_2d_T},                  {coords, vec2_i32},                      {level, i32},                      }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_2d_array_T},            {coords, vec2_i32}, {array_index, i32},  {level, i32},                      }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_3d_T},                  {coords, vec3_i32},                      {level, i32},                      }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_ms_2d_T},               {coords, vec2_i32},                                    {sample_index, i32}, }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_ms_2d_array_T},         {coords, vec2_i32}, {array_index, i32},                {sample_index, i32}, }); // NOLINT
+  Register(I::kTextureLoad, f32,        {{t, tex_depth_2d},              {coords, vec2_i32},                      {level, i32},                      }); // NOLINT
+  Register(I::kTextureLoad, f32,        {{t, tex_depth_2d_array},        {coords, vec2_i32}, {array_index, i32},  {level, i32},                      }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_storage_ro_1d_FT},      {coords, i32},                                                              }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_storage_ro_2d_FT},      {coords, vec2_i32},                                                         }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_storage_ro_2d_array_FT},{coords, vec2_i32}, {array_index, i32},                                     }); // NOLINT
+  Register(I::kTextureLoad, vec4_T,     {{t, tex_storage_ro_3d_FT},      {coords, vec3_i32},                                                         }); // NOLINT
+  Register(I::kTextureLoad, vec4_f32,   {{t, tex_external},              {coords, vec2_i32}                                                          }); // NOLINT
 
   // clang-format on
 
diff --git a/src/intrinsic_table_test.cc b/src/intrinsic_table_test.cc
index a7a9f00..deae118 100644
--- a/src/intrinsic_table_test.cc
+++ b/src/intrinsic_table_test.cc
@@ -18,6 +18,7 @@
 #include "src/program_builder.h"
 #include "src/sem/access_control_type.h"
 #include "src/sem/depth_texture_type.h"
+#include "src/sem/external_texture_type.h"
 #include "src/sem/multisampled_texture_type.h"
 #include "src/sem/sampled_texture_type.h"
 #include "src/sem/storage_texture_type.h"
@@ -285,6 +286,20 @@
                           Parameter{ty.i32(), Parameter::Usage::kLevel}));
 }
 
+TEST_F(IntrinsicTableTest, MatchExternalTexture) {
+  auto* tex = create<sem::ExternalTexture>();
+  auto result = table->Lookup(*this, IntrinsicType::kTextureLoad,
+                              {tex, ty.vec2<i32>()}, Source{});
+  ASSERT_NE(result.intrinsic, nullptr);
+  ASSERT_EQ(result.diagnostics.str(), "");
+  EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kTextureLoad);
+  EXPECT_THAT(result.intrinsic->ReturnType(), ty.vec4<f32>());
+  EXPECT_THAT(
+      result.intrinsic->Parameters(),
+      ElementsAre(Parameter{tex, Parameter::Usage::kTexture},
+                  Parameter{ty.vec2<i32>(), Parameter::Usage::kCoords}));
+}
+
 TEST_F(IntrinsicTableTest, MatchROStorageTexture) {
   auto tex = ty.storage_texture(ast::TextureDimension::k2d,
                                 ast::ImageFormat::kR16Float);
@@ -431,7 +446,7 @@
   ASSERT_EQ(result.diagnostics.str(),
             R"(error: no matching call to textureDimensions(bool, bool)
 
-25 candidate functions:
+26 candidate functions:
   textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
   textureDimensions(texture : texture_2d_array<T>, level : i32) -> vec2<i32>
   textureDimensions(texture : texture_3d<T>, level : i32) -> vec3<i32>
@@ -457,6 +472,7 @@
   textureDimensions(texture : texture_storage_2d<F>) -> vec2<i32>
   textureDimensions(texture : texture_storage_2d_array<F>) -> vec2<i32>
   textureDimensions(texture : texture_storage_3d<F>) -> vec3<i32>
+  textureDimensions(texture : texture_external) -> vec2<i32>
 )");
 }
 
@@ -468,7 +484,7 @@
       result.diagnostics.str(),
       R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
 
-25 candidate functions:
+26 candidate functions:
   textureDimensions(texture : texture_depth_2d, level : i32) -> vec2<i32>
   textureDimensions(texture : texture_depth_2d) -> vec2<i32>
   textureDimensions(texture : texture_2d<T>, level : i32) -> vec2<i32>
@@ -494,6 +510,7 @@
   textureDimensions(texture : texture_storage_2d<F>) -> vec2<i32>
   textureDimensions(texture : texture_storage_2d_array<F>) -> vec2<i32>
   textureDimensions(texture : texture_storage_3d<F>) -> vec3<i32>
+  textureDimensions(texture : texture_external) -> vec2<i32>
 )");
 }
 
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index b224788..bfc34c0 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -650,6 +650,9 @@
     return {Token::Type::kTextureDepthCubeArray, source,
             "texture_depth_cube_array"};
   }
+  if (str == "texture_external") {
+    return {Token::Type::kTextureExternal, source, "texture_external"};
+  }
   if (str == "texture_multisampled_2d") {
     return {Token::Type::kTextureMultisampled2d, source,
             "texture_multisampled_2d"};
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 4783c4f..f09b481 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -26,6 +26,7 @@
 #include "src/reader/wgsl/lexer.h"
 #include "src/sem/access_control_type.h"
 #include "src/sem/depth_texture_type.h"
+#include "src/sem/external_texture_type.h"
 #include "src/sem/multisampled_texture_type.h"
 #include "src/sem/sampled_texture_type.h"
 
@@ -516,6 +517,10 @@
   if (type.matched)
     return type.value;
 
+  type = external_texture_type();
+  if (type.matched)
+    return type.value;
+
   auto dim = sampled_texture_type();
   if (dim.matched) {
     const char* use = "sampled texture type";
@@ -600,6 +605,16 @@
   return Failure::kNoMatch;
 }
 
+// external_texture_type
+//  : TEXTURE_EXTERNAL
+Maybe<sem::Type*> ParserImpl::external_texture_type() {
+  if (match(Token::Type::kTextureExternal)) {
+    return builder_.create<sem::ExternalTexture>();
+  }
+
+  return Failure::kNoMatch;
+}
+
 // multisampled_texture_type
 //  : TEXTURE_MULTISAMPLED_2D
 Maybe<ast::TextureDimension> ParserImpl::multisampled_texture_type() {
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index fe42a76..6b01db2 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -414,6 +414,9 @@
   /// Parses a `depth_texture_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
   Maybe<sem::Type*> depth_texture_type();
+  /// Parses a 'texture_external_type' grammar element
+  /// @returns the parsed Type or nullptr if none matched
+  Maybe<sem::Type*> external_texture_type();
   /// Parses a `image_storage_type` grammar element
   /// @param use a description of what was being parsed if an error was raised
   /// @returns returns the image format or kNone if none matched.
diff --git a/src/reader/wgsl/parser_impl_external_texture_type_test.cc b/src/reader/wgsl/parser_impl_external_texture_type_test.cc
new file mode 100644
index 0000000..92a8738
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_external_texture_type_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2021 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 "src/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+namespace {
+
+TEST_F(ParserImplTest, ExternalTextureType_Invalid) {
+  auto p = parser("1234");
+  auto t = p->external_texture_type();
+  EXPECT_FALSE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, ExternalTextureType) {
+  auto p = parser("texture_external");
+  auto t = p->external_texture_type();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+}
+
+}  // namespace
+}  // namespace wgsl
+}  // namespace reader
+}  // namespace tint
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index a89c2ef..f131cd9 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -269,6 +269,8 @@
       return "texture_depth_cube";
     case Token::Type::kTextureDepthCubeArray:
       return "texture_depth_cube_array";
+    case Token::Type::kTextureExternal:
+      return "texture_external";
     case Token::Type::kTextureMultisampled2d:
       return "texture_multisampled_2d";
     case Token::Type::kTextureSampled1d:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index 6e4581f..10c6b0e 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -277,6 +277,8 @@
     kTextureDepthCube,
     /// A 'texture_depth_cube_array'
     kTextureDepthCubeArray,
+    /// A 'texture_external'
+    kTextureExternal,
     /// A 'texture_multisampled_2d'
     kTextureMultisampled2d,
     /// A 'texture_1d'
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 106056e..ce361dd 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -416,6 +416,7 @@
     "../src/reader/wgsl/parser_impl_error_msg_test.cc",
     "../src/reader/wgsl/parser_impl_error_resync_test.cc",
     "../src/reader/wgsl/parser_impl_exclusive_or_expression_test.cc",
+    "../src/reader/wgsl/parser_impl_external_texture_type_test.cc",
     "../src/reader/wgsl/parser_impl_for_stmt_test.cc",
     "../src/reader/wgsl/parser_impl_function_decl_test.cc",
     "../src/reader/wgsl/parser_impl_function_decoration_list_test.cc",