Add texture_storage_yy parsing.

This CL adds support for the decorated texture storage tokens. The old
_ro_ and _wo_ tokens still exist until downstream users are updated.

Bug: tint:286
Change-Id: I4ddc50be3b22bd3feeab41b3b4fe4ded63e6e59a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/37780
Auto-Submit: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/docs/translations.md b/docs/translations.md
index 5688952..270a1a4 100644
--- a/docs/translations.md
+++ b/docs/translations.md
@@ -128,17 +128,17 @@
 | texture_depth_cube | OpTypeImage Cube Depth=1 Sampled=1 | depthcube&lt;float, access::sample&gt; | TextureCube |
 | texture_depth_cube_array | OpTypeImage Cube Depth=1 Arrayed=1 Sampled=1 | depthcube_array&lt;float, access::sample&gt; | TextureCubeArray |
 | | | |
-| texture_storage_ro_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2| texture1d&lt;type, access::read&gt; | RWTexture1D |
-| texture_storage_ro_1d_array&lt;image_storage_type&gt; | OpTypeImage 1D Arrayed=1 Sampled=2 | texture1d_array&lt;type, access::read&gt; | RWTexture1DArray |
-| texture_storage_ro_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=2 | texture2d&lt;type, access::read&gt; | RWTexture2D |
-| texture_storage_ro_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::read&gt; | RWTexture2DArray |
-| texture_storage_ro_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::read&gt; | RWTexture3D |
+| texture_storage_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2| texture1d&lt;type, access::read&gt; | RWTexture1D |
+| texture_storage_1d_array&lt;image_storage_type&gt; | OpTypeImage 1D Arrayed=1 Sampled=2 | texture1d_array&lt;type, access::read&gt; | RWTexture1DArray |
+| texture_storage_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=2 | texture2d&lt;type, access::read&gt; | RWTexture2D |
+| texture_storage_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::read&gt; | RWTexture2DArray |
+| texture_storage_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::read&gt; | RWTexture3D |
 | | | |
-| texture_storage_wo_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2 | texture1d&lt;type, access::write&gt; | RWTexture1D |
-| texture_storage_wo_1d_array&lt;image_storage_type&gt; | OpTypeImage 1D Arrayed=1 Sampled=2 | texture1d_array&lt;type, access::write&gt; | RWTexture1DArray |
-| texture_storage_wo_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=1 | texture2d&lt;type, access::write&gt; | RWTexture2D |
-| texture_storage_wo_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::write&gt; | RWTexture2DArray |
-| texture_storage_wo_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::write&gt; | RWTexture3D|
+| texture_storage_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2 | texture1d&lt;type, access::write&gt; | RWTexture1D |
+| texture_storage_1d_array&lt;image_storage_type&gt; | OpTypeImage 1D Arrayed=1 Sampled=2 | texture1d_array&lt;type, access::write&gt; | RWTexture1DArray |
+| texture_storage_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=1 | texture2d&lt;type, access::write&gt; | RWTexture2D |
+| texture_storage_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::write&gt; | RWTexture2DArray |
+| texture_storage_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::write&gt; | RWTexture3D|
 
 # Short-circuting
 ## HLSL
diff --git a/fuzzers/dictionary.txt b/fuzzers/dictionary.txt
index 48b82de..359d58f 100644
--- a/fuzzers/dictionary.txt
+++ b/fuzzers/dictionary.txt
@@ -86,11 +86,11 @@
 "texture_depth_cube"
 "texture_depth_cube_array"
 "texture_multisampled_2d"
-"texture_storage_ro_1d"
-"texture_storage_ro_2d_array"
-"texture_storage_ro_2d"
-"texture_storage_ro_2d_array"
-"texture_storage_ro_3d"
+"texture_storage_1d"
+"texture_storage_2d_array"
+"texture_storage_2d"
+"texture_storage_2d_array"
+"texture_storage_3d"
 "texture_1d"
 "texture_1d_array"
 "texture_2d"
@@ -98,11 +98,6 @@
 "texture_3d"
 "texture_cube"
 "texture_cube_array"
-"texture_wo_1d"
-"texture_wo_2d_array"
-"texture_wo_2d"
-"texture_wo_2d_array"
-"texture_wo_3d"
 "true"
 "type"
 "u32"
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
index ba25bdc..7d06193 100644
--- a/src/ast/intrinsic_texture_helper_test.cc
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -435,7 +435,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageRO1d,
-          "textureDimensions(t : texture_storage_ro_1d<rgba32float>) -> i32",
+          "textureDimensions(t : texture_storage_1d<rgba32float>) -> i32",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
           type::TextureDimension::k1d,
@@ -445,7 +445,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageRO1dArray,
-          "textureDimensions(t : texture_storage_ro_1d_array<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_1d_array<rgba32float>) -> "
           "i32",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -456,7 +456,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageRO2d,
-          "textureDimensions(t : texture_storage_ro_2d<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_2d<rgba32float>) -> "
           "vec2<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -467,7 +467,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageRO2dArray,
-          "textureDimensions(t : texture_storage_ro_2d_array<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_2d_array<rgba32float>) -> "
           "vec2<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -478,7 +478,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageRO3d,
-          "textureDimensions(t : texture_storage_ro_3d<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_3d<rgba32float>) -> "
           "vec3<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -489,7 +489,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageWO1d,
-          "textureDimensions(t : texture_storage_wo_1d<rgba32float>) -> i32",
+          "textureDimensions(t : texture_storage_1d<rgba32float>) -> i32",
           ast::AccessControl::kWriteOnly,
           ast::type::ImageFormat::kRgba32Float,
           type::TextureDimension::k1d,
@@ -499,7 +499,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageWO1dArray,
-          "textureDimensions(t : texture_storage_wo_1d_array<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_1d_array<rgba32float>) -> "
           "i32",
           ast::AccessControl::kWriteOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -510,7 +510,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageWO2d,
-          "textureDimensions(t : texture_storage_wo_2d<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_2d<rgba32float>) -> "
           "vec2<i32>",
           ast::AccessControl::kWriteOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -521,7 +521,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageWO2dArray,
-          "textureDimensions(t : texture_storage_wo_2d_array<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_2d_array<rgba32float>) -> "
           "vec2<i32>",
           ast::AccessControl::kWriteOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -532,7 +532,7 @@
       },
       {
           ValidTextureOverload::kDimensionsStorageWO3d,
-          "textureDimensions(t : texture_storage_wo_3d<rgba32float>) -> "
+          "textureDimensions(t : texture_storage_3d<rgba32float>) -> "
           "vec3<i32>",
           ast::AccessControl::kWriteOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -2047,7 +2047,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO1dRgba32float,
-          "textureLoad(t      : texture_storage_ro_1d<rgba32float>,\n"
+          "textureLoad(t      : texture_storage_1d<rgba32float>,\n"
           "            coords : i32) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -2062,7 +2062,7 @@
       {
           ValidTextureOverload::kLoadStorageRO1dArrayRgba32float,
           "textureLoad(t           : "
-          "texture_storage_ro_1d_array<rgba32float>,\n"
+          "texture_storage_1d_array<rgba32float>,\n"
           "            coords      : i32,\n"
           "            array_index : i32) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
@@ -2078,7 +2078,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba8unorm,
-          "textureLoad(t           : texture_storage_ro_2d<rgba8unorm>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba8unorm>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba8Unorm,
@@ -2092,7 +2092,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba8snorm,
-          "textureLoad(t           : texture_storage_ro_2d<rgba8snorm>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba8snorm>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba8Snorm,
@@ -2106,7 +2106,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba8uint,
-          "textureLoad(t           : texture_storage_ro_2d<rgba8uint>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba8uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba8Uint,
@@ -2120,7 +2120,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba8sint,
-          "textureLoad(t           : texture_storage_ro_2d<rgba8sint>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba8sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba8Sint,
@@ -2134,7 +2134,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba16uint,
-          "textureLoad(t           : texture_storage_ro_2d<rgba16uint>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba16uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba16Uint,
@@ -2148,7 +2148,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba16sint,
-          "textureLoad(t           : texture_storage_ro_2d<rgba16sint>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba16sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba16Sint,
@@ -2162,7 +2162,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba16float,
-          "textureLoad(t           : texture_storage_ro_2d<rgba16float>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba16float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba16Float,
@@ -2176,7 +2176,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dR32uint,
-          "textureLoad(t           : texture_storage_ro_2d<r32uint>,\n"
+          "textureLoad(t           : texture_storage_2d<r32uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kR32Uint,
@@ -2190,7 +2190,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dR32sint,
-          "textureLoad(t           : texture_storage_ro_2d<r32sint>,\n"
+          "textureLoad(t           : texture_storage_2d<r32sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kR32Sint,
@@ -2204,7 +2204,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dR32float,
-          "textureLoad(t           : texture_storage_ro_2d<r32float>,\n"
+          "textureLoad(t           : texture_storage_2d<r32float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kR32Float,
@@ -2218,7 +2218,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRg32uint,
-          "textureLoad(t           : texture_storage_ro_2d<rg32uint>,\n"
+          "textureLoad(t           : texture_storage_2d<rg32uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRg32Uint,
@@ -2232,7 +2232,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRg32sint,
-          "textureLoad(t           : texture_storage_ro_2d<rg32sint>,\n"
+          "textureLoad(t           : texture_storage_2d<rg32sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRg32Sint,
@@ -2246,7 +2246,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRg32float,
-          "textureLoad(t           : texture_storage_ro_2d<rg32float>,\n"
+          "textureLoad(t           : texture_storage_2d<rg32float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRg32Float,
@@ -2260,7 +2260,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba32uint,
-          "textureLoad(t           : texture_storage_ro_2d<rgba32uint>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba32uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Uint,
@@ -2274,7 +2274,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba32sint,
-          "textureLoad(t           : texture_storage_ro_2d<rgba32sint>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba32sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Sint,
@@ -2288,7 +2288,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO2dRgba32float,
-          "textureLoad(t           : texture_storage_ro_2d<rgba32float>,\n"
+          "textureLoad(t           : texture_storage_2d<rgba32float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -2303,7 +2303,7 @@
       {
           ValidTextureOverload::kLoadStorageRO2dArrayRgba32float,
           "textureLoad(t           : "
-          "texture_storage_ro_2d_array<rgba32float>,\n"
+          "texture_storage_2d_array<rgba32float>,\n"
           "            coords      : vec2<i32>,\n"
           "            array_index : i32) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
@@ -2319,7 +2319,7 @@
       },
       {
           ValidTextureOverload::kLoadStorageRO3dRgba32float,
-          "textureLoad(t      : texture_storage_ro_3d<rgba32float>,\n"
+          "textureLoad(t      : texture_storage_3d<rgba32float>,\n"
           "            coords : vec3<i32>) -> vec4<f32>",
           ast::AccessControl::kReadOnly,
           ast::type::ImageFormat::kRgba32Float,
@@ -2333,7 +2333,7 @@
       },
       {
           ValidTextureOverload::kStoreWO1dRgba32float,
-          "textureStore(t      : texture_storage_wo_1d<F>,\n"
+          "textureStore(t      : texture_storage_1d<F>,\n"
           "             coords : i32,\n"
           "             value  : vec4<T>) -> void",
           ast::AccessControl::kWriteOnly,
@@ -2349,7 +2349,7 @@
       },
       {
           ValidTextureOverload::kStoreWO1dArrayRgba32float,
-          "textureStore(t           : texture_storage_wo_1d_array<F>,\n"
+          "textureStore(t           : texture_storage_1d_array<F>,\n"
           "             coords      : i32,\n"
           "             array_index : i32,\n"
           "             value       : vec4<T>) -> void",
@@ -2367,7 +2367,7 @@
       },
       {
           ValidTextureOverload::kStoreWO2dRgba32float,
-          "textureStore(t      : texture_storage_wo_2d<F>,\n"
+          "textureStore(t      : texture_storage_2d<F>,\n"
           "             coords : vec2<i32>,\n"
           "             value  : vec4<T>) -> void",
           ast::AccessControl::kWriteOnly,
@@ -2383,7 +2383,7 @@
       },
       {
           ValidTextureOverload::kStoreWO2dArrayRgba32float,
-          "textureStore(t           : texture_storage_wo_2d_array<F>,\n"
+          "textureStore(t           : texture_storage_2d_array<F>,\n"
           "             coords      : vec2<i32>,\n"
           "             array_index : i32,\n"
           "             value       : vec4<T>) -> void",
@@ -2401,7 +2401,7 @@
       },
       {
           ValidTextureOverload::kStoreWO3dRgba32float,
-          "textureStore(t      : texture_storage_wo_3d<F>,\n"
+          "textureStore(t      : texture_storage_3d<F>,\n"
           "             coords : vec3<i32>,\n"
           "             value  : vec4<T>) -> void",
           ast::AccessControl::kWriteOnly,
diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc
index 8405dc1..a434f93 100644
--- a/src/ast/module_clone_test.cc
+++ b/src/ast/module_clone_test.cc
@@ -45,10 +45,10 @@
 var<uniform> g0 : u32 = 20u;
 var<out> g1 : f32 = 123.0;
 var<uniform> g2 : texture_2d<f32>;
-var<uniform> g3 : texture_storage_ro_2d<r32uint>;
-var<uniform> g4 : texture_storage_wo_2d<rg32float>;
-var<uniform> g5 : texture_storage_ro_2d<r32uint>;
-var<uniform> g6 : texture_storage_wo_2d<rg32float>;
+var<uniform> g3 : [[access(read)]] texture_storage_2d<r32uint>;
+var<uniform> g4 : [[access(write)]] texture_storage_2d<rg32float>;
+var<uniform> g5 : [[access(read)]] texture_storage_2d<r32uint>;
+var<uniform> g6 : [[access(write)]] texture_storage_2d<rg32float>;
 
 [[builtin(position)]] var<uniform> g7 : vec3<f32>;
 [[set(10), binding(20)]] var<storage_buffer> g7 : S;
@@ -150,7 +150,7 @@
   std::string src_wgsl;
   {
     writer::wgsl::Generator src_gen(std::move(src));
-    ASSERT_TRUE(src_gen.Generate());
+    ASSERT_TRUE(src_gen.Generate()) << src_gen.error();
     src_wgsl = src_gen.result();
   }
 
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 9b8f584..3d781a1 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -712,6 +712,43 @@
     return {Token::Type::kTextureSampledCubeArray, source,
             "texture_sampled_cube_array"};
   }
+  if (str == "texture_storage_1d") {
+    return {Token::Type::kTextureStorage1d, source, "texture_storage_1d"};
+  }
+  if (str == "texture_storage_1d_array") {
+    return {Token::Type::kTextureStorage1dArray, source,
+            "texture_storage_1d_array"};
+  }
+  if (str == "texture_storage_2d") {
+    return {Token::Type::kTextureStorage2d, source, "texture_storage_2d"};
+  }
+  if (str == "texture_storage_2d_array") {
+    return {Token::Type::kTextureStorage2dArray, source,
+            "texture_storage_2d_array"};
+  }
+  if (str == "texture_storage_3d") {
+    return {Token::Type::kTextureStorage3d, source, "texture_storage_3d"};
+  }
+  if (str == "texture_storage_wo_1d") {
+    return {Token::Type::kTextureStorageWriteonly1d, source,
+            "texture_storage_wo_1d"};
+  }
+  if (str == "texture_storage_wo_1d_array") {
+    return {Token::Type::kTextureStorageWriteonly1dArray, source,
+            "texture_storage_wo_1d_array"};
+  }
+  if (str == "texture_storage_wo_2d") {
+    return {Token::Type::kTextureStorageWriteonly2d, source,
+            "texture_storage_wo_2d"};
+  }
+  if (str == "texture_storage_wo_2d_array") {
+    return {Token::Type::kTextureStorageWriteonly2dArray, source,
+            "texture_storage_wo_2d_array"};
+  }
+  if (str == "texture_storage_wo_3d") {
+    return {Token::Type::kTextureStorageWriteonly3d, source,
+            "texture_storage_wo_3d"};
+  }
   if (str == "texture_storage_ro_1d") {
     return {Token::Type::kTextureStorageReadonly1d, source,
             "texture_storage_ro_1d"};
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index 8668960..36ead45 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -564,6 +564,13 @@
         TokenData{"texture_ro_2d_array",
                   Token::Type::kTextureStorageReadonly2dArray},
         TokenData{"texture_ro_3d", Token::Type::kTextureStorageReadonly3d},
+        TokenData{"texture_storage_1d", Token::Type::kTextureStorage1d},
+        TokenData{"texture_storage_1d_array",
+                  Token::Type::kTextureStorage1dArray},
+        TokenData{"texture_storage_2d", Token::Type::kTextureStorage2d},
+        TokenData{"texture_storage_2d_array",
+                  Token::Type::kTextureStorage2dArray},
+        TokenData{"texture_storage_3d", Token::Type::kTextureStorage3d},
         TokenData{"texture_storage_ro_1d",
                   Token::Type::kTextureStorageReadonly1d},
         TokenData{"texture_storage_ro_1d_array",
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 831479a..50d6f17 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -96,6 +96,7 @@
 const char kComputeStage[] = "compute";
 
 const char kReadAccessControl[] = "read";
+const char kWriteAccessControl[] = "write";
 const char kReadWriteAccessControl[] = "read_write";
 
 ast::Builtin ident_to_builtin(const std::string& str) {
@@ -536,9 +537,24 @@
     if (format.errored)
       return Failure::kErrored;
 
+    return module_.create<ast::type::StorageTexture>(storage.value,
+                                                     format.value);
+  }
+
+  // DEPRECATED
+  auto ac_storage = storage_texture_type_access_control();
+  if (ac_storage.matched) {
+    const char* use = "storage texture type";
+
+    auto format =
+        expect_lt_gt_block(use, [&] { return expect_image_storage_type(use); });
+
+    if (format.errored)
+      return Failure::kErrored;
+
     return module_.create<ast::type::AccessControl>(
-        storage->second, module_.create<ast::type::StorageTexture>(
-                             storage->first, format.value));
+        ac_storage->second, module_.create<ast::type::StorageTexture>(
+                                ac_storage->first, format.value));
   }
 
   return Failure::kNoMatch;
@@ -601,7 +617,29 @@
 }
 
 // storage_texture_type
-//  : TEXTURE_RO_1D
+//  : TEXTURE_STORAGE_1D
+//  | TEXTURE_STORAGE_1D_ARRAY
+//  | TEXTURE_STORAGE_2D
+//  | TEXTURE_STORAGE_2D_ARRAY
+//  | TEXTURE_STORAGE_3D
+Maybe<ast::type::TextureDimension> ParserImpl::storage_texture_type() {
+  if (match(Token::Type::kTextureStorage1d))
+    return ast::type::TextureDimension::k1d;
+  if (match(Token::Type::kTextureStorage1dArray))
+    return ast::type::TextureDimension::k1dArray;
+  if (match(Token::Type::kTextureStorage2d))
+    return ast::type::TextureDimension::k2d;
+  if (match(Token::Type::kTextureStorage2dArray))
+    return ast::type::TextureDimension::k2dArray;
+  if (match(Token::Type::kTextureStorage3d))
+    return ast::type::TextureDimension::k3d;
+
+  return Failure::kNoMatch;
+}
+
+// DEPRECATED
+// storage_texture_type
+//  | TEXTURE_RO_1D
 //  | TEXTURE_RO_1D_ARRAY
 //  | TEXTURE_RO_2D
 //  | TEXTURE_RO_2D_ARRAY
@@ -622,7 +660,7 @@
 //  | TEXTURE_STORAGE_WO_2D_ARRAY
 //  | TEXTURE_STORAGE_WO_3D
 Maybe<std::pair<ast::type::TextureDimension, ast::AccessControl>>
-ParserImpl::storage_texture_type() {
+ParserImpl::storage_texture_type_access_control() {
   using Ret = std::pair<ast::type::TextureDimension, ast::AccessControl>;
   if (match(Token::Type::kTextureStorageReadonly1d)) {
     return Ret{ast::type::TextureDimension::k1d, ast::AccessControl::kReadOnly};
@@ -893,6 +931,8 @@
 
   if (ident.value == kReadAccessControl)
     return {ast::AccessControl::kReadOnly, ident.source};
+  if (ident.value == kWriteAccessControl)
+    return {ast::AccessControl::kWriteOnly, ident.source};
   if (ident.value == kReadWriteAccessControl)
     return {ast::AccessControl::kReadWrite, ident.source};
 
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 9a4e478..385e890 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -427,10 +427,14 @@
   /// @returns returns the sample texture dimension or kNone if none matched.
   Maybe<ast::type::TextureDimension> sampled_texture_type();
   /// Parses a `storage_texture_type` grammar element
+  /// @returns returns the storage texture dimension.
+  /// Returns kNone if none matched.
+  Maybe<ast::type::TextureDimension> storage_texture_type();
+  /// Parses a deprecated `storage_texture_type` grammar element
   /// @returns returns the storage texture dimension and the storage access.
   ///          Returns kNone and kRead if none matched.
   Maybe<std::pair<ast::type::TextureDimension, ast::AccessControl>>
-  storage_texture_type();
+  storage_texture_type_access_control();
   /// Parses a `depth_texture_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
   Maybe<ast::type::Type*> depth_texture_type();
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index d969e72..f24eed4 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -651,27 +651,55 @@
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingLessThan) {
+  EXPECT("var x : [[access(read)]] texture_storage_2d;",
+         "test.wgsl:1:44 error: expected '<' for storage texture type\n"
+         "var x : [[access(read)]] texture_storage_2d;\n"
+         "                                           ^\n");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingGreaterThan) {
+  EXPECT("var x : [[access(read)]] texture_storage_2d<r8uint;",
+         "test.wgsl:1:51 error: expected '>' for storage texture type\n"
+         "var x : [[access(read)]] texture_storage_2d<r8uint;\n"
+         "                                                  ^\n");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingSubtype) {
+  EXPECT("var x : [[access(read)]] texture_storage_2d<>;",
+         "test.wgsl:1:45 error: invalid format for storage texture type\n"
+         "var x : [[access(read)]] texture_storage_2d<>;\n"
+         "                                            ^\n");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingInvalidSubtype) {
+  EXPECT("var x : [[access(read)]] texture_storage_2d<1>;",
+         "test.wgsl:1:45 error: invalid format for storage texture type\n"
+         "var x : [[access(read)]] texture_storage_2d<1>;\n"
+         "                                            ^\n");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingLessThan_old) {
   EXPECT("var x : texture_storage_ro_2d;",
          "test.wgsl:1:30 error: expected '<' for storage texture type\n"
          "var x : texture_storage_ro_2d;\n"
          "                             ^\n");
 }
 
-TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingGreaterThan) {
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingGreaterThan_old) {
   EXPECT("var x : texture_storage_ro_2d<r8uint;",
          "test.wgsl:1:37 error: expected '>' for storage texture type\n"
          "var x : texture_storage_ro_2d<r8uint;\n"
          "                                    ^\n");
 }
 
-TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingSubtype) {
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingSubtype_old) {
   EXPECT("var x : texture_storage_ro_2d<>;",
          "test.wgsl:1:31 error: invalid format for storage texture type\n"
          "var x : texture_storage_ro_2d<>;\n"
          "                              ^\n");
 }
 
-TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingInvalidSubtype) {
+TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingInvalidSubtype_old) {
   EXPECT("var x : texture_storage_ro_2d<1>;",
          "test.wgsl:1:31 error: invalid format for storage texture type\n"
          "var x : texture_storage_ro_2d<1>;\n"
diff --git a/src/reader/wgsl/parser_impl_storage_texture_type_test.cc b/src/reader/wgsl/parser_impl_storage_texture_type_test.cc
index ea6744d..82d97fe 100644
--- a/src/reader/wgsl/parser_impl_storage_texture_type_test.cc
+++ b/src/reader/wgsl/parser_impl_storage_texture_type_test.cc
@@ -30,9 +30,62 @@
   EXPECT_FALSE(p->has_error());
 }
 
+TEST_F(ParserImplTest, StorageTextureType_1d) {
+  auto p = parser("texture_storage_1d");
+  auto t = p->storage_texture_type();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::TextureDimension::k1d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, StorageTextureType_1dArray) {
+  auto p = parser("texture_storage_1d_array");
+  auto t = p->storage_texture_type();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::TextureDimension::k1dArray);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, StorageTextureType_2d) {
+  auto p = parser("texture_storage_2d");
+  auto t = p->storage_texture_type();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::TextureDimension::k2d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, StorageTextureType_2dArray) {
+  auto p = parser("texture_storage_2d_array");
+  auto t = p->storage_texture_type();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::TextureDimension::k2dArray);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, StorageTextureType_3d) {
+  auto p = parser("texture_storage_3d");
+  auto t = p->storage_texture_type();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_EQ(t.value, ast::type::TextureDimension::k3d);
+  EXPECT_FALSE(p->has_error());
+}
+
+TEST_F(ParserImplTest, StorageTextureType_Invalid_old) {
+  auto p = parser("abc");
+  auto t = p->storage_texture_type_access_control();
+  EXPECT_FALSE(t.matched);
+  EXPECT_FALSE(t.errored);
+  EXPECT_FALSE(p->has_error());
+}
+
 TEST_F(ParserImplTest, StorageTextureType_Readonly1d_Old) {
   auto p = parser("texture_ro_1d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1d);
@@ -42,7 +95,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Readonly1dArray_Old) {
   auto p = parser("texture_ro_1d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1dArray);
@@ -52,7 +105,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Readonly2d_Old) {
   auto p = parser("texture_ro_2d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2d);
@@ -62,7 +115,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Readonly2dArray_Old) {
   auto p = parser("texture_ro_2d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2dArray);
@@ -72,7 +125,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Readonly3d_Old) {
   auto p = parser("texture_ro_3d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k3d);
@@ -82,7 +135,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Writeonly1d_Old) {
   auto p = parser("texture_wo_1d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1d);
@@ -92,7 +145,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Writeonly1dArray_Old) {
   auto p = parser("texture_wo_1d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1dArray);
@@ -102,7 +155,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Writeonly2d_Old) {
   auto p = parser("texture_wo_2d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2d);
@@ -112,7 +165,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Writeonly2dArray_Old) {
   auto p = parser("texture_wo_2d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2dArray);
@@ -122,7 +175,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_Writeonly3d_Old) {
   auto p = parser("texture_wo_3d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k3d);
@@ -132,7 +185,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_ro_1d) {
   auto p = parser("texture_storage_ro_1d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1d);
@@ -142,7 +195,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_ro_1dArray) {
   auto p = parser("texture_storage_ro_1d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1dArray);
@@ -152,7 +205,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_ro_2d) {
   auto p = parser("texture_storage_ro_2d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2d);
@@ -162,7 +215,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_ro_2dArray) {
   auto p = parser("texture_storage_ro_2d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2dArray);
@@ -172,7 +225,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_ro_3d) {
   auto p = parser("texture_storage_ro_3d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k3d);
@@ -182,7 +235,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_wo_1d) {
   auto p = parser("texture_storage_wo_1d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1d);
@@ -192,7 +245,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_wo_1dArray) {
   auto p = parser("texture_storage_wo_1d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k1dArray);
@@ -202,7 +255,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_wo_2d) {
   auto p = parser("texture_storage_wo_2d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2d);
@@ -212,7 +265,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_wo_2dArray) {
   auto p = parser("texture_storage_wo_2d_array");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k2dArray);
@@ -222,7 +275,7 @@
 
 TEST_F(ParserImplTest, StorageTextureType_wo_3d) {
   auto p = parser("texture_storage_wo_3d");
-  auto t = p->storage_texture_type();
+  auto t = p->storage_texture_type_access_control();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
   EXPECT_EQ(std::get<0>(t.value), ast::type::TextureDimension::k3d);
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 967a57c..88bc7d3 100644
--- a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
+++ b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
@@ -374,7 +374,8 @@
   EXPECT_EQ(p->error(), "1:22: expected '>' for storage texture type");
 }
 
-TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm) {
+TEST_F(ParserImplTest,
+       TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm_old) {
   auto p = parser("texture_storage_ro_1d<r8unorm>");
   auto ac = p->texture_sampler_types();
   ASSERT_FALSE(p->has_error()) << p->error();
@@ -395,7 +396,8 @@
             ast::type::TextureDimension::k1d);
 }
 
-TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
+TEST_F(ParserImplTest,
+       TextureSamplerTypes_StorageTexture_Writeonly2dR16Float_old) {
   auto p = parser("texture_storage_wo_2d<r16float>");
   auto ac = p->texture_sampler_types();
   ASSERT_FALSE(p->has_error()) << p->error();
@@ -416,7 +418,7 @@
             ast::type::TextureDimension::k2d);
 }
 
-TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType) {
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType_old) {
   auto p = parser("texture_storage_ro_1d<abc>");
   auto t = p->texture_sampler_types();
   EXPECT_EQ(t.value, nullptr);
@@ -425,7 +427,7 @@
   EXPECT_EQ(p->error(), "1:23: invalid format for storage texture type");
 }
 
-TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingType) {
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingType_old) {
   auto p = parser("texture_storage_ro_1d<>");
   auto t = p->texture_sampler_types();
   EXPECT_EQ(t.value, nullptr);
@@ -434,7 +436,7 @@
   EXPECT_EQ(p->error(), "1:23: invalid format for storage texture type");
 }
 
-TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingLessThan) {
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingLessThan_old) {
   auto p = parser("texture_storage_ro_1d");
   auto t = p->texture_sampler_types();
   EXPECT_EQ(t.value, nullptr);
@@ -443,7 +445,8 @@
   EXPECT_EQ(p->error(), "1:22: expected '<' for storage texture type");
 }
 
-TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingGreaterThan) {
+TEST_F(ParserImplTest,
+       TextureSamplerTypes_StorageTexture_MissingGreaterThan_old) {
   auto p = parser("texture_storage_ro_1d<r8unorm");
   auto t = p->texture_sampler_types();
   EXPECT_EQ(t.value, nullptr);
@@ -452,6 +455,73 @@
   EXPECT_EQ(p->error(), "1:30: expected '>' for storage texture type");
 }
 
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm) {
+  auto p = parser("texture_storage_1d<r8unorm>");
+  auto t = p->texture_sampler_types();
+  ASSERT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  ASSERT_NE(t.value, nullptr);
+
+  ASSERT_TRUE(t->Is<ast::type::Texture>());
+  ASSERT_TRUE(t->Is<ast::type::StorageTexture>());
+  EXPECT_EQ(t->As<ast::type::StorageTexture>()->image_format(),
+            ast::type::ImageFormat::kR8Unorm);
+  EXPECT_EQ(t->As<ast::type::Texture>()->dim(),
+            ast::type::TextureDimension::k1d);
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
+  auto p = parser("texture_storage_2d<r16float>");
+  auto t = p->texture_sampler_types();
+  ASSERT_FALSE(p->has_error()) << p->error();
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  ASSERT_NE(t.value, nullptr);
+
+  ASSERT_TRUE(t->Is<ast::type::Texture>());
+  ASSERT_TRUE(t->Is<ast::type::StorageTexture>());
+  EXPECT_EQ(t->As<ast::type::StorageTexture>()->image_format(),
+            ast::type::ImageFormat::kR16Float);
+  EXPECT_EQ(t->As<ast::type::Texture>()->dim(),
+            ast::type::TextureDimension::k2d);
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType) {
+  auto p = parser("texture_storage_1d<abc>");
+  auto t = p->texture_sampler_types();
+  EXPECT_EQ(t.value, nullptr);
+  EXPECT_FALSE(t.matched);
+  EXPECT_TRUE(t.errored);
+  EXPECT_EQ(p->error(), "1:20: invalid format for storage texture type");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingType) {
+  auto p = parser("texture_storage_1d<>");
+  auto t = p->texture_sampler_types();
+  EXPECT_EQ(t.value, nullptr);
+  EXPECT_FALSE(t.matched);
+  EXPECT_TRUE(t.errored);
+  EXPECT_EQ(p->error(), "1:20: invalid format for storage texture type");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingLessThan) {
+  auto p = parser("texture_storage_1d");
+  auto t = p->texture_sampler_types();
+  EXPECT_EQ(t.value, nullptr);
+  EXPECT_FALSE(t.matched);
+  EXPECT_TRUE(t.errored);
+  EXPECT_EQ(p->error(), "1:19: expected '<' for storage texture type");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingGreaterThan) {
+  auto p = parser("texture_storage_1d<r8unorm");
+  auto t = p->texture_sampler_types();
+  EXPECT_EQ(t.value, nullptr);
+  EXPECT_FALSE(t.matched);
+  EXPECT_TRUE(t.errored);
+  EXPECT_EQ(p->error(), "1:27: expected '>' for storage texture type");
+}
 }  // namespace
 }  // namespace wgsl
 }  // namespace reader
diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index 0e57c1d..b8763fe 100644
--- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -82,6 +82,36 @@
   ASSERT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
 }
 
+TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithTextureAccessDeco_Read) {
+  auto p = parser("my_var : [[access(read)]] texture_storage_1d<r32float>");
+
+  auto decl = p->expect_variable_ident_decl("test");
+  ASSERT_FALSE(p->has_error()) << p->error();
+  ASSERT_FALSE(decl.errored);
+  ASSERT_EQ(decl->name, "my_var");
+  ASSERT_NE(decl->type, nullptr);
+  ASSERT_TRUE(decl->type->Is<ast::type::AccessControl>());
+  EXPECT_TRUE(decl->type->As<ast::type::AccessControl>()->IsReadOnly());
+  ASSERT_TRUE(decl->type->As<ast::type::AccessControl>()
+                  ->type()
+                  ->Is<ast::type::StorageTexture>());
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithTextureAccessDeco_Write) {
+  auto p = parser("my_var : [[access(write)]] texture_storage_1d<r32float>");
+
+  auto decl = p->expect_variable_ident_decl("test");
+  ASSERT_FALSE(p->has_error()) << p->error();
+  ASSERT_FALSE(decl.errored);
+  ASSERT_EQ(decl->name, "my_var");
+  ASSERT_NE(decl->type, nullptr);
+  ASSERT_TRUE(decl->type->Is<ast::type::AccessControl>());
+  EXPECT_TRUE(decl->type->As<ast::type::AccessControl>()->IsWriteOnly());
+  ASSERT_TRUE(decl->type->As<ast::type::AccessControl>()
+                  ->type()
+                  ->Is<ast::type::StorageTexture>());
+}
+
 TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_Read) {
   auto p = parser("my_var : [[access(read)]] S");
 
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index 4338ac2..1ba7385 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -291,6 +291,16 @@
       return "texture_cube";
     case Token::Type::kTextureSampledCubeArray:
       return "texture_cube_array";
+    case Token::Type::kTextureStorage1d:
+      return "texture_storage_1d";
+    case Token::Type::kTextureStorage1dArray:
+      return "texture_storage_1d_array";
+    case Token::Type::kTextureStorage2d:
+      return "texture_storage_2d";
+    case Token::Type::kTextureStorage2dArray:
+      return "texture_storage_2d_array";
+    case Token::Type::kTextureStorage3d:
+      return "texture_storage_3d";
     case Token::Type::kTextureStorageWriteonly1d:
       return "texture_storage_wo_1d";
     case Token::Type::kTextureStorageWriteonly1dArray:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index e56a560..58d6b52 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -302,6 +302,16 @@
     kTextureSampledCube,
     /// A 'texture_cube_array'
     kTextureSampledCubeArray,
+    /// A 'texture_storage_1d'
+    kTextureStorage1d,
+    /// A 'texture_storage_1d_array'
+    kTextureStorage1dArray,
+    /// A 'texture_storage_2d'
+    kTextureStorage2d,
+    /// A 'texture_storage_2d_array'
+    kTextureStorage2dArray,
+    /// A 'texture_storage_3d'
+    kTextureStorage3d,
     /// A 'texture_wo_1d'
     kTextureStorageWriteonly1d,
     /// A 'texture_wo_2d_array'
@@ -641,6 +651,20 @@
   bool IsTextureMultisampled2d() const {
     return type_ == Type::kTextureMultisampled2d;
   }
+  /// @returns true if token is a 'texture_storage_1d'
+  bool IsTextureStorage1d() const { return type_ == Type::kTextureStorage1d; }
+  /// @returns true if token is a 'texture_storage_1d_array'
+  bool IsTextureStorage1dArray() const {
+    return type_ == Type::kTextureStorage1dArray;
+  }
+  /// @returns true if token is a 'texture_storage_2d'
+  bool IsTextureStorage2d() const { return type_ == Type::kTextureStorage2d; }
+  /// @returns true if token is a 'texture_storage_2d_array'
+  bool IsTextureStorage2dArray() const {
+    return type_ == Type::kTextureStorage2dArray;
+  }
+  /// @returns true if token is a 'texture_storage_3d'
+  bool IsTextureStorage3d() const { return type_ == Type::kTextureStorage3d; }
   /// @returns true if token is a 'texture_storage_ro_1d'
   bool IsTextureStorageReadonly1d() const {
     return type_ == Type::kTextureStorageReadonly1d;
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index ccfcca4..8d3585b 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -498,7 +498,7 @@
 }
 
 TEST_F(BuilderTest, GlobalVar_TextureStorageReadOnly) {
-  // var<uniform_constant> a : texture_storage_ro_2d<r32uint>;
+  // var<uniform_constant> a : [[access(read)]] texture_storage_2d<r32uint>;
 
   ast::type::StorageTexture type(ast::type::TextureDimension::k2d,
                                  ast::type::ImageFormat::kR32Uint);
@@ -519,7 +519,7 @@
 }
 
 TEST_F(BuilderTest, GlobalVar_TextureStorageWriteOnly) {
-  // var<uniform_constant> a : texture_storage_wo_2d<r32uint>;
+  // var<uniform_constant> a : [[access(write)]] texture_storage_2d<r32uint>;
 
   ast::type::StorageTexture type(ast::type::TextureDimension::k2d,
                                  ast::type::ImageFormat::kR32Uint);
@@ -542,8 +542,8 @@
 // Check that multiple texture_storage types with different access modifiers
 // only produces a single OpTypeImage.
 TEST_F(BuilderTest, GlobalVar_TextureStorageWithDifferentAccess) {
-  // var<uniform_constant> a : texture_storage_ro_2d<r32uint>;
-  // var<uniform_constant> b : texture_storage_wo_2d<r32uint>;
+  // var<uniform_constant> a : [[access(read)]] texture_storage_2d<r32uint>;
+  // var<uniform_constant> b : [[access(write)]] texture_storage_2d<r32uint>;
 
   ast::type::StorageTexture st(ast::type::TextureDimension::k2d,
                                ast::type::ImageFormat::kR32Uint);
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index 9b67d42..aabb8fd 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -401,39 +401,23 @@
 bool GeneratorImpl::EmitType(ast::type::Type* type) {
   std::string storage_texture_access = "";
   if (auto* ac = type->As<ast::type::AccessControl>()) {
-    // TODO(dsinclair): Removing the special casing for storage textures when
-    // we've converted over to parsing texture_storage_yy.
-    if (ac->type()->Is<ast::type::StorageTexture>()) {
-      if (ac->access_control() == ast::AccessControl::kReadOnly) {
-        storage_texture_access = "ro_";
-      } else if (ac->access_control() == ast::AccessControl::kWriteOnly) {
-        storage_texture_access = "wo_";
-      } else {
-        error_ = "unknown storage texture access";
-        return false;
-      }
-
-      // We want to generate the wrapped type in this case.
-      type = ac->type();
+    out_ << "[[access(";
+    if (ac->IsReadOnly()) {
+      out_ << "read";
+    } else if (ac->IsWriteOnly()) {
+      out_ << "write";
+    } else if (ac->IsReadWrite()) {
+      out_ << "read_write";
     } else {
-      out_ << "[[access(";
-      if (ac->IsReadOnly()) {
-        out_ << "read";
-      } else if (ac->IsReadWrite()) {
-        out_ << "read_write";
-      } else {
-        error_ = "invalid access control";
-        return false;
-      }
-      out_ << ")]]" << std::endl;
-      if (!EmitType(ac->type())) {
-        return false;
-      }
-      return true;
+      error_ = "invalid access control";
+      return false;
     }
-  }
-
-  if (auto* alias = type->As<ast::type::Alias>()) {
+    out_ << ")]]" << std::endl;
+    if (!EmitType(ac->type())) {
+      return false;
+    }
+    return true;
+  } else if (auto* alias = type->As<ast::type::Alias>()) {
     out_ << module_.SymbolToName(alias->symbol());
   } else if (auto* ary = type->As<ast::type::Array>()) {
     for (auto* deco : ary->decorations()) {
@@ -487,8 +471,8 @@
       /* nothing to emit */
     } else if (texture->Is<ast::type::MultisampledTexture>()) {
       out_ << "multisampled_";
-    } else if (auto* storage = texture->As<ast::type::StorageTexture>()) {
-      out_ << "storage_" << storage_texture_access;
+    } else if (texture->Is<ast::type::StorageTexture>()) {
+      out_ << "storage_";
     } else {
       error_ = "unknown texture type";
       return false;
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc
index 2b8a0e8..cff995d 100644
--- a/src/writer/wgsl/generator_impl_type_test.cc
+++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -332,40 +332,50 @@
     WgslGeneratorImplTest,
     WgslGenerator_StorageTextureTest,
     testing::Values(
-        StorageTextureData{
-            ast::type::ImageFormat::kR8Unorm, ast::type::TextureDimension::k1d,
-            ast::AccessControl::kReadOnly, "texture_storage_ro_1d<r8unorm>"},
         StorageTextureData{ast::type::ImageFormat::kR8Unorm,
-                           ast::type::TextureDimension::k1dArray,
+                           ast::type::TextureDimension::k1d,
                            ast::AccessControl::kReadOnly,
-                           "texture_storage_ro_1d_array<r8unorm>"},
+                           "[[access(read)]]\ntexture_storage_1d<r8unorm>"},
         StorageTextureData{
-            ast::type::ImageFormat::kR8Unorm, ast::type::TextureDimension::k2d,
-            ast::AccessControl::kReadOnly, "texture_storage_ro_2d<r8unorm>"},
+            ast::type::ImageFormat::kR8Unorm,
+            ast::type::TextureDimension::k1dArray,
+            ast::AccessControl::kReadOnly,
+            "[[access(read)]]\ntexture_storage_1d_array<r8unorm>"},
         StorageTextureData{ast::type::ImageFormat::kR8Unorm,
-                           ast::type::TextureDimension::k2dArray,
+                           ast::type::TextureDimension::k2d,
                            ast::AccessControl::kReadOnly,
-                           "texture_storage_ro_2d_array<r8unorm>"},
+                           "[[access(read)]]\ntexture_storage_2d<r8unorm>"},
         StorageTextureData{
-            ast::type::ImageFormat::kR8Unorm, ast::type::TextureDimension::k3d,
-            ast::AccessControl::kReadOnly, "texture_storage_ro_3d<r8unorm>"},
-        StorageTextureData{
-            ast::type::ImageFormat::kR8Unorm, ast::type::TextureDimension::k1d,
-            ast::AccessControl::kWriteOnly, "texture_storage_wo_1d<r8unorm>"},
+            ast::type::ImageFormat::kR8Unorm,
+            ast::type::TextureDimension::k2dArray,
+            ast::AccessControl::kReadOnly,
+            "[[access(read)]]\ntexture_storage_2d_array<r8unorm>"},
         StorageTextureData{ast::type::ImageFormat::kR8Unorm,
-                           ast::type::TextureDimension::k1dArray,
-                           ast::AccessControl::kWriteOnly,
-                           "texture_storage_wo_1d_array<r8unorm>"},
-        StorageTextureData{
-            ast::type::ImageFormat::kR8Unorm, ast::type::TextureDimension::k2d,
-            ast::AccessControl::kWriteOnly, "texture_storage_wo_2d<r8unorm>"},
+                           ast::type::TextureDimension::k3d,
+                           ast::AccessControl::kReadOnly,
+                           "[[access(read)]]\ntexture_storage_3d<r8unorm>"},
         StorageTextureData{ast::type::ImageFormat::kR8Unorm,
-                           ast::type::TextureDimension::k2dArray,
+                           ast::type::TextureDimension::k1d,
                            ast::AccessControl::kWriteOnly,
-                           "texture_storage_wo_2d_array<r8unorm>"},
+                           "[[access(write)]]\ntexture_storage_1d<r8unorm>"},
         StorageTextureData{
-            ast::type::ImageFormat::kR8Unorm, ast::type::TextureDimension::k3d,
-            ast::AccessControl::kWriteOnly, "texture_storage_wo_3d<r8unorm>"}));
+            ast::type::ImageFormat::kR8Unorm,
+            ast::type::TextureDimension::k1dArray,
+            ast::AccessControl::kWriteOnly,
+            "[[access(write)]]\ntexture_storage_1d_array<r8unorm>"},
+        StorageTextureData{ast::type::ImageFormat::kR8Unorm,
+                           ast::type::TextureDimension::k2d,
+                           ast::AccessControl::kWriteOnly,
+                           "[[access(write)]]\ntexture_storage_2d<r8unorm>"},
+        StorageTextureData{
+            ast::type::ImageFormat::kR8Unorm,
+            ast::type::TextureDimension::k2dArray,
+            ast::AccessControl::kWriteOnly,
+            "[[access(write)]]\ntexture_storage_2d_array<r8unorm>"},
+        StorageTextureData{ast::type::ImageFormat::kR8Unorm,
+                           ast::type::TextureDimension::k3d,
+                           ast::AccessControl::kWriteOnly,
+                           "[[access(write)]]\ntexture_storage_3d<r8unorm>"}));
 
 struct ImageFormatData {
   ast::type::ImageFormat fmt;