wgsl: Deprecate [[access]] decorations

Handle access control on var declarations instead of via [[access]]
decorations. This change does the minimal work to migrate the WGSL
parser over to the new syntax. Additional changes will be needed
to correctly generate defaulted access qualifiers, as well as
validating access usage.

The [[access]] decorations are still supported by the WGSL parser,
with new deprecated warnings, but not for aliases. Example:
   var x : [[access(x)]] alias_to_struct;

Making this work is far more effort than I want to dedicate to backwards
compatibility, and I do not beleive any real-world usage will be doing
this.

Still TODO:
* Adding access control as the optional, third parameter to ptr<>.
* Calculating default accesses for the various storage types.
* Validating usage of variables against the different accesses.

Bug: tint:846
Change-Id: If8ca82e5d16ec319ecd01f9a2cafffd930963bde
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/53088
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 9498f0a..57b816d 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -292,8 +292,8 @@
 
 libtint_source_set("libtint_core_all_src") {
   sources = [
-    "ast/access_control.cc",
-    "ast/access_control.h",
+    "ast/access.cc",
+    "ast/access.h",
     "ast/access_decoration.cc",
     "ast/access_decoration.h",
     "ast/alias.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 268b037..16d5a90 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -38,8 +38,8 @@
 
 set(TINT_LIB_SRCS
   ../include/tint/tint.h
-  ast/access_control.cc
-  ast/access_control.h
+  ast/access.cc
+  ast/access.h
   ast/access_decoration.cc
   ast/access_decoration.h
   ast/alias.cc
@@ -496,7 +496,6 @@
 
 if(${TINT_BUILD_TESTS})
   set(TINT_TEST_SRCS
-    ast/access_control_test.cc
     ast/access_decoration_test.cc
     ast/alias_test.cc
     ast/array_accessor_expression_test.cc
@@ -748,7 +747,7 @@
       reader/wgsl/parser_impl_variable_decoration_test.cc
       reader/wgsl/parser_impl_variable_ident_decl_test.cc
       reader/wgsl/parser_impl_variable_stmt_test.cc
-      reader/wgsl/parser_impl_variable_storage_decoration_test.cc
+      reader/wgsl/parser_impl_variable_qualifier_test.cc
       reader/wgsl/token_test.cc
     )
   endif()
diff --git a/src/ast/access.cc b/src/ast/access.cc
new file mode 100644
index 0000000..a95753f
--- /dev/null
+++ b/src/ast/access.cc
@@ -0,0 +1,43 @@
+// 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 "src/ast/access.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, Access access) {
+  switch (access) {
+    case ast::Access::kUndefined: {
+      out << "undefined";
+      break;
+    }
+    case ast::Access::kRead: {
+      out << "read";
+      break;
+    }
+    case ast::Access::kReadWrite: {
+      out << "read_write";
+      break;
+    }
+    case ast::Access::kWrite: {
+      out << "write";
+      break;
+    }
+  }
+  return out;
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/ast/access.h b/src/ast/access.h
new file mode 100644
index 0000000..1faddbf
--- /dev/null
+++ b/src/ast/access.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef SRC_AST_ACCESS_H_
+#define SRC_AST_ACCESS_H_
+
+#include <ostream>
+#include <string>
+
+namespace tint {
+namespace ast {
+
+/// The access control settings
+enum Access {
+  /// Not declared in the source
+  kUndefined,
+  /// Read only
+  kRead,
+  /// Write only
+  kWrite,
+  /// Read write
+  kReadWrite
+};
+
+/// @param out the std::ostream to write to
+/// @param access the Access
+/// @return the std::ostream so calls can be chained
+std::ostream& operator<<(std::ostream& out, Access access);
+
+/// [DEPRECATED]: Old name in use by Dawn.
+using AccessControl = Access;
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_AST_ACCESS_H_
diff --git a/src/ast/access_control.cc b/src/ast/access_control.cc
deleted file mode 100644
index ad86d2c..0000000
--- a/src/ast/access_control.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// 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 "src/ast/access_control.h"
-
-#include "src/program_builder.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::ast::AccessControl);
-
-namespace tint {
-namespace ast {
-
-AccessControl::AccessControl(ProgramID program_id,
-                             const Source& source,
-                             Access access,
-                             const Type* subtype)
-    : Base(program_id, source), access_(access), subtype_(subtype) {
-  TINT_ASSERT(subtype_);
-  TINT_ASSERT(!subtype_->Is<AccessControl>());
-}
-
-AccessControl::AccessControl(AccessControl&&) = default;
-
-AccessControl::~AccessControl() = default;
-
-std::string AccessControl::type_name() const {
-  std::string name = "__access_control_";
-  switch (access_) {
-    case ast::AccessControl::kRead:
-      name += "read_only";
-      break;
-    case ast::AccessControl::kWrite:
-      name += "write_only";
-      break;
-    case ast::AccessControl::kReadWrite:
-      name += "read_write";
-      break;
-  }
-  return name + subtype_->type_name();
-}
-
-std::string AccessControl::FriendlyName(const SymbolTable& symbols) const {
-  std::ostringstream out;
-  out << "[[access(";
-  switch (access_) {
-    case ast::AccessControl::kRead:
-      out << "read";
-      break;
-    case ast::AccessControl::kWrite:
-      out << "write";
-      break;
-    case ast::AccessControl::kReadWrite:
-      out << "read_write";
-      break;
-  }
-  out << ")]] " << subtype_->FriendlyName(symbols);
-  return out.str();
-}
-
-AccessControl* AccessControl::Clone(CloneContext* ctx) const {
-  // Clone arguments outside of create() call to have deterministic ordering
-  auto src = ctx->Clone(source());
-  auto* ty = ctx->Clone(type());
-  return ctx->dst->create<AccessControl>(src, access_, ty);
-}
-
-std::ostream& operator<<(std::ostream& out, AccessControl::Access access) {
-  switch (access) {
-    case ast::AccessControl::kRead: {
-      out << "read_only";
-      break;
-    }
-    case ast::AccessControl::kReadWrite: {
-      out << "read_write";
-      break;
-    }
-    case ast::AccessControl::kWrite: {
-      out << "write_only";
-      break;
-    }
-  }
-  return out;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/access_control.h b/src/ast/access_control.h
deleted file mode 100644
index 705faca..0000000
--- a/src/ast/access_control.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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.
-
-#ifndef SRC_AST_ACCESS_CONTROL_H_
-#define SRC_AST_ACCESS_CONTROL_H_
-
-#include <ostream>
-#include <string>
-
-#include "src/ast/type.h"
-
-namespace tint {
-namespace ast {
-
-/// An access control type. Holds an access setting and pointer to another type.
-class AccessControl : public Castable<AccessControl, Type> {
- public:
-  /// The access control settings
-  enum Access {
-    /// Read only
-    kRead,
-    /// Write only
-    kWrite,
-    /// Read write
-    kReadWrite
-  };
-
-  /// Constructor
-  /// @param program_id the identifier of the program that owns this node
-  /// @param source the source of this node
-  /// @param access the access control setting
-  /// @param subtype the access controlled type
-  AccessControl(ProgramID program_id,
-                const Source& source,
-                Access access,
-                const Type* subtype);
-  /// Move constructor
-  AccessControl(AccessControl&&);
-  ~AccessControl() override;
-
-  /// @returns true if the access control is read only
-  bool IsReadOnly() const { return access_ == Access::kRead; }
-  /// @returns true if the access control is write only
-  bool IsWriteOnly() const { return access_ == Access::kWrite; }
-  /// @returns true if the access control is read/write
-  bool IsReadWrite() const { return access_ == Access::kReadWrite; }
-
-  /// @returns the access control value
-  Access access_control() const { return access_; }
-  /// @returns the subtype type
-  Type* type() const { return const_cast<Type*>(subtype_); }
-
-  /// @returns the name for this type
-  std::string type_name() const override;
-
-  /// @param symbols the program's symbol table
-  /// @returns the name for this type that closely resembles how it would be
-  /// declared in WGSL.
-  std::string FriendlyName(const SymbolTable& symbols) const override;
-
-  /// Clones this type and all transitive types using the `CloneContext` `ctx`.
-  /// @param ctx the clone context
-  /// @return the newly cloned type
-  AccessControl* Clone(CloneContext* ctx) const override;
-
- private:
-  Access const access_;
-  const Type* const subtype_;
-};
-
-/// @param out the std::ostream to write to
-/// @param access the AccessControl
-/// @return the std::ostream so calls can be chained
-std::ostream& operator<<(std::ostream& out, AccessControl::Access access);
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_ACCESS_CONTROL_H_
diff --git a/src/ast/access_control_test.cc b/src/ast/access_control_test.cc
deleted file mode 100644
index d8fca38..0000000
--- a/src/ast/access_control_test.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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 "src/ast/access_control.h"
-
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
-#include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
-
-namespace tint {
-namespace ast {
-namespace {
-
-using AstAccessControlTest = TestHelper;
-
-TEST_F(AstAccessControlTest, Create) {
-  auto* u32 = create<U32>();
-  auto* a = create<AccessControl>(AccessControl::kReadWrite, u32);
-  EXPECT_TRUE(a->IsReadWrite());
-  EXPECT_EQ(a->type(), u32);
-}
-
-TEST_F(AstAccessControlTest, AccessRead) {
-  auto* i32 = create<I32>();
-  auto* ac = create<AccessControl>(AccessControl::kRead, i32);
-  EXPECT_TRUE(ac->IsReadOnly());
-  EXPECT_FALSE(ac->IsWriteOnly());
-  EXPECT_FALSE(ac->IsReadWrite());
-
-  EXPECT_EQ(ac->type_name(), "__access_control_read_only__i32");
-}
-
-TEST_F(AstAccessControlTest, AccessWrite) {
-  auto* i32 = create<I32>();
-  auto* ac = create<AccessControl>(AccessControl::kWrite, i32);
-  EXPECT_FALSE(ac->IsReadOnly());
-  EXPECT_TRUE(ac->IsWriteOnly());
-  EXPECT_FALSE(ac->IsReadWrite());
-
-  EXPECT_EQ(ac->type_name(), "__access_control_write_only__i32");
-}
-
-TEST_F(AstAccessControlTest, AccessReadWrite) {
-  auto* i32 = create<I32>();
-  auto* ac = create<AccessControl>(AccessControl::kReadWrite, i32);
-  EXPECT_FALSE(ac->IsReadOnly());
-  EXPECT_FALSE(ac->IsWriteOnly());
-  EXPECT_TRUE(ac->IsReadWrite());
-
-  EXPECT_EQ(ac->type_name(), "__access_control_read_write__i32");
-}
-
-TEST_F(AstAccessControlTest, FriendlyNameReadOnly) {
-  auto* i32 = create<I32>();
-  auto* ac = create<AccessControl>(AccessControl::kRead, i32);
-  EXPECT_EQ(ac->FriendlyName(Symbols()), "[[access(read)]] i32");
-}
-
-TEST_F(AstAccessControlTest, FriendlyNameWriteOnly) {
-  auto* i32 = create<I32>();
-  auto* ac = create<AccessControl>(AccessControl::kWrite, i32);
-  EXPECT_EQ(ac->FriendlyName(Symbols()), "[[access(write)]] i32");
-}
-
-TEST_F(AstAccessControlTest, FriendlyNameReadWrite) {
-  auto* i32 = create<I32>();
-  auto* ac = create<AccessControl>(AccessControl::kReadWrite, i32);
-  EXPECT_EQ(ac->FriendlyName(Symbols()), "[[access(read_write)]] i32");
-}
-
-}  // namespace
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/access_decoration.cc b/src/ast/access_decoration.cc
index e00ace6..865422a 100644
--- a/src/ast/access_decoration.cc
+++ b/src/ast/access_decoration.cc
@@ -23,7 +23,7 @@
 
 AccessDecoration::AccessDecoration(ProgramID program_id,
                                    const Source& source,
-                                   AccessControl::Access val)
+                                   Access val)
     : Base(program_id, source), value_(val) {}
 
 AccessDecoration::~AccessDecoration() = default;
diff --git a/src/ast/access_decoration.h b/src/ast/access_decoration.h
index 894bb69..2b385e9 100644
--- a/src/ast/access_decoration.h
+++ b/src/ast/access_decoration.h
@@ -15,26 +15,25 @@
 #ifndef SRC_AST_ACCESS_DECORATION_H_
 #define SRC_AST_ACCESS_DECORATION_H_
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/decoration.h"
 
 namespace tint {
 namespace ast {
 
 /// An access decoration
+/// [DEPRECATED]: TODO(crbug.com/tint/846): Remove this class
 class AccessDecoration : public Castable<AccessDecoration, Decoration> {
  public:
   /// constructor
   /// @param program_id the identifier of the program that owns this node
   /// @param source the source of this decoration
   /// @param value the access value
-  AccessDecoration(ProgramID program_id,
-                   const Source& source,
-                   AccessControl::Access value);
+  AccessDecoration(ProgramID program_id, const Source& source, Access value);
   ~AccessDecoration() override;
 
   /// @returns the access control value
-  AccessControl::Access value() const { return value_; }
+  Access value() const { return value_; }
 
   /// Outputs the decoration to the given stream
   /// @param sem the semantic info for the program
@@ -51,7 +50,7 @@
   AccessDecoration* Clone(CloneContext* ctx) const override;
 
  private:
-  AccessControl::Access const value_;
+  Access const value_;
 };
 
 }  // namespace ast
diff --git a/src/ast/access_decoration_test.cc b/src/ast/access_decoration_test.cc
index 047b170..ecb6e0d 100644
--- a/src/ast/access_decoration_test.cc
+++ b/src/ast/access_decoration_test.cc
@@ -23,13 +23,13 @@
 using AccessDecorationTest = TestHelper;
 
 TEST_F(AccessDecorationTest, Creation) {
-  auto* d = create<AccessDecoration>(ast::AccessControl::kWrite);
-  EXPECT_EQ(ast::AccessControl::kWrite, d->value());
+  auto* d = create<AccessDecoration>(ast::Access::kWrite);
+  EXPECT_EQ(ast::Access::kWrite, d->value());
 }
 
 TEST_F(AccessDecorationTest, ToStr) {
-  auto* d = create<AccessDecoration>(ast::AccessControl::kRead);
-  EXPECT_EQ(str(d), R"(AccessDecoration{read_only}
+  auto* d = create<AccessDecoration>(ast::Access::kRead);
+  EXPECT_EQ(str(d), R"(AccessDecoration{read}
 )");
 }
 
diff --git a/src/ast/alias_test.cc b/src/ast/alias_test.cc
index 1dc9710..9adebbd 100644
--- a/src/ast/alias_test.cc
+++ b/src/ast/alias_test.cc
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "src/ast/alias.h"
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/array.h"
 #include "src/ast/bool.h"
 #include "src/ast/f32.h"
@@ -76,23 +76,6 @@
   EXPECT_EQ(aapaa->UnwrapAll(), u32);
 }
 
-TEST_F(AstAliasTest, UnwrapAll_AccessControlPointer) {
-  auto* u32 = create<U32>();
-  auto* a = create<AccessControl>(AccessControl::kRead, u32);
-  auto* pa = create<Pointer>(a, StorageClass::kUniform);
-  EXPECT_EQ(pa->type(), a);
-  EXPECT_EQ(pa->UnwrapAll(), u32);
-}
-
-TEST_F(AstAliasTest, UnwrapAll_PointerAccessControl) {
-  auto* u32 = create<U32>();
-  auto* p = create<Pointer>(u32, StorageClass::kUniform);
-  auto* a = create<AccessControl>(AccessControl::kRead, p);
-
-  EXPECT_EQ(a->type(), p);
-  EXPECT_EQ(a->UnwrapAll(), u32);
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/array_test.cc b/src/ast/array_test.cc
index dac1542..5e8b9c8 100644
--- a/src/ast/array_test.cc
+++ b/src/ast/array_test.cc
@@ -13,19 +13,8 @@
 // limitations under the License.
 
 #include "src/ast/array.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
+
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/ast_type.cc b/src/ast/ast_type.cc
index a9ea9d1..6702bd9 100644
--- a/src/ast/ast_type.cc
+++ b/src/ast/ast_type.cc
@@ -14,7 +14,6 @@
 
 #include "src/ast/type.h"
 
-#include "src/ast/access_control.h"
 #include "src/ast/alias.h"
 #include "src/ast/bool.h"
 #include "src/ast/f32.h"
@@ -43,8 +42,6 @@
   while (true) {
     if (auto* alias = type->As<Alias>()) {
       type = alias->type();
-    } else if (auto* access = type->As<AccessControl>()) {
-      type = access->type();
     } else if (auto* ptr = type->As<Pointer>()) {
       type = ptr->type();
     } else {
diff --git a/src/ast/bool_test.cc b/src/ast/bool_test.cc
index d1668c7..7c8a32e 100644
--- a/src/ast/bool_test.cc
+++ b/src/ast/bool_test.cc
@@ -13,19 +13,8 @@
 // limitations under the License.
 
 #include "src/ast/bool.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
+
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/depth_texture_test.cc b/src/ast/depth_texture_test.cc
index a018e4a..d7b7249 100644
--- a/src/ast/depth_texture_test.cc
+++ b/src/ast/depth_texture_test.cc
@@ -13,22 +13,8 @@
 // limitations under the License.
 
 #include "src/ast/depth_texture.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampled_texture.h"
-#include "src/ast/sampler.h"
-#include "src/ast/storage_texture.h"
-#include "src/ast/struct.h"
+
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/external_texture_test.cc b/src/ast/external_texture_test.cc
index b8d75d1..ff11eae 100644
--- a/src/ast/external_texture_test.cc
+++ b/src/ast/external_texture_test.cc
@@ -14,22 +14,7 @@
 
 #include "src/ast/external_texture.h"
 
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/depth_texture.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/storage_texture.h"
-#include "src/ast/struct.h"
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/f32_test.cc b/src/ast/f32_test.cc
index 220af9d..12eeec8 100644
--- a/src/ast/f32_test.cc
+++ b/src/ast/f32_test.cc
@@ -13,19 +13,8 @@
 // limitations under the License.
 
 #include "src/ast/f32.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
+
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/function_test.cc b/src/ast/function_test.cc
index a634e6f..61ee8a6 100644
--- a/src/ast/function_test.cc
+++ b/src/ast/function_test.cc
@@ -189,6 +189,7 @@
   VariableConst{
     var
     none
+    undefined
     __i32
   }
 )
diff --git a/src/ast/i32_test.cc b/src/ast/i32_test.cc
index 5f1ae0a..6c224ca 100644
--- a/src/ast/i32_test.cc
+++ b/src/ast/i32_test.cc
@@ -14,19 +14,7 @@
 
 #include "src/ast/i32.h"
 
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
index 82cf623..f5af1fb 100644
--- a/src/ast/intrinsic_texture_helper_test.cc
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -62,7 +62,7 @@
 TextureOverloadCase::TextureOverloadCase(
     ValidTextureOverload o,
     const char* d,
-    AccessControl::Access access,
+    Access acc,
     ast::ImageFormat i,
     ast::TextureDimension dims,
     TextureDataType datatype,
@@ -71,7 +71,7 @@
     : overload(o),
       description(d),
       texture_kind(TextureKind::kStorage),
-      access_control(access),
+      access(acc),
       image_format(i),
       texture_dimension(dims),
       texture_data_type(datatype),
@@ -124,7 +124,7 @@
     out << "<unused>";
   }
   out << "\n";
-  out << "access_control:    " << data.access_control << "\n";
+  out << "access:    " << data.access << "\n";
   out << "image_format:      " << data.image_format << "\n";
   out << "texture_dimension: " << data.texture_dimension << "\n";
   out << "texture_data_type: " << data.texture_data_type << "\n";
@@ -157,23 +157,22 @@
       return b->Global("texture",
                        b->ty.sampled_texture(texture_dimension,
                                              buildResultVectorComponentType(b)),
-                       ast::StorageClass::kNone, nullptr, decos);
+                       decos);
 
     case ast::intrinsic::test::TextureKind::kDepth:
       return b->Global("texture", b->ty.depth_texture(texture_dimension),
-                       ast::StorageClass::kNone, nullptr, decos);
+                       decos);
 
     case ast::intrinsic::test::TextureKind::kMultisampled:
       return b->Global(
           "texture",
           b->ty.multisampled_texture(texture_dimension,
                                      buildResultVectorComponentType(b)),
-          ast::StorageClass::kNone, nullptr, decos);
+          decos);
 
     case ast::intrinsic::test::TextureKind::kStorage: {
-      auto* st = b->ty.storage_texture(texture_dimension, image_format);
-      auto* ac = b->ty.access(access_control, st);
-      return b->Global("texture", ac, ast::StorageClass::kNone, nullptr, decos);
+      auto* st = b->ty.storage_texture(texture_dimension, image_format, access);
+      return b->Global("texture", st, decos);
     }
   }
 
@@ -187,8 +186,7 @@
       b->create<ast::GroupDecoration>(0),
       b->create<ast::BindingDecoration>(1),
   };
-  return b->Global("sampler", b->ty.sampler(sampler_kind),
-                   ast::StorageClass::kNone, nullptr, decos);
+  return b->Global("sampler", b->ty.sampler(sampler_kind), decos);
 }
 
 std::vector<TextureOverloadCase> TextureOverloadCase::ValidCases() {
@@ -405,7 +403,7 @@
       {
           ValidTextureOverload::kDimensionsStorageRO1d,
           "textureDimensions(t : texture_storage_1d<rgba32float>) -> i32",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k1d,
           TextureDataType::kF32,
@@ -416,7 +414,7 @@
           ValidTextureOverload::kDimensionsStorageRO2d,
           "textureDimensions(t : texture_storage_2d<rgba32float>) -> "
           "vec2<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -427,7 +425,7 @@
           ValidTextureOverload::kDimensionsStorageRO2dArray,
           "textureDimensions(t : texture_storage_2d_array<rgba32float>) -> "
           "vec2<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2dArray,
           TextureDataType::kF32,
@@ -438,7 +436,7 @@
           ValidTextureOverload::kDimensionsStorageRO3d,
           "textureDimensions(t : texture_storage_3d<rgba32float>) -> "
           "vec3<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k3d,
           TextureDataType::kF32,
@@ -448,7 +446,7 @@
       {
           ValidTextureOverload::kDimensionsStorageWO1d,
           "textureDimensions(t : texture_storage_1d<rgba32float>) -> i32",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k1d,
           TextureDataType::kF32,
@@ -459,7 +457,7 @@
           ValidTextureOverload::kDimensionsStorageWO2d,
           "textureDimensions(t : texture_storage_2d<rgba32float>) -> "
           "vec2<i32>",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -470,7 +468,7 @@
           ValidTextureOverload::kDimensionsStorageWO2dArray,
           "textureDimensions(t : texture_storage_2d_array<rgba32float>) -> "
           "vec2<i32>",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2dArray,
           TextureDataType::kF32,
@@ -481,7 +479,7 @@
           ValidTextureOverload::kDimensionsStorageWO3d,
           "textureDimensions(t : texture_storage_3d<rgba32float>) -> "
           "vec3<i32>",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k3d,
           TextureDataType::kF32,
@@ -531,7 +529,7 @@
       {
           ValidTextureOverload::kNumLayersStorageWO2dArray,
           "textureNumLayers(t : texture_storage_2d_array<rgba32float>) -> i32",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2dArray,
           TextureDataType::kF32,
@@ -1887,7 +1885,7 @@
           ValidTextureOverload::kLoadStorageRO1dRgba32float,
           "textureLoad(t      : texture_storage_1d<rgba32float>,\n"
           "            coords : i32) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k1d,
           TextureDataType::kF32,
@@ -1901,7 +1899,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba8unorm,
           "textureLoad(t           : texture_storage_2d<rgba8unorm>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba8Unorm,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -1915,7 +1913,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba8snorm,
           "textureLoad(t           : texture_storage_2d<rgba8snorm>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba8Snorm,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -1929,7 +1927,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba8uint,
           "textureLoad(t           : texture_storage_2d<rgba8uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba8Uint,
           ast::TextureDimension::k2d,
           TextureDataType::kU32,
@@ -1943,7 +1941,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba8sint,
           "textureLoad(t           : texture_storage_2d<rgba8sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba8Sint,
           ast::TextureDimension::k2d,
           TextureDataType::kI32,
@@ -1957,7 +1955,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba16uint,
           "textureLoad(t           : texture_storage_2d<rgba16uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba16Uint,
           ast::TextureDimension::k2d,
           TextureDataType::kU32,
@@ -1971,7 +1969,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba16sint,
           "textureLoad(t           : texture_storage_2d<rgba16sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba16Sint,
           ast::TextureDimension::k2d,
           TextureDataType::kI32,
@@ -1985,7 +1983,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba16float,
           "textureLoad(t           : texture_storage_2d<rgba16float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba16Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -1999,7 +1997,7 @@
           ValidTextureOverload::kLoadStorageRO2dR32uint,
           "textureLoad(t           : texture_storage_2d<r32uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kR32Uint,
           ast::TextureDimension::k2d,
           TextureDataType::kU32,
@@ -2013,7 +2011,7 @@
           ValidTextureOverload::kLoadStorageRO2dR32sint,
           "textureLoad(t           : texture_storage_2d<r32sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kR32Sint,
           ast::TextureDimension::k2d,
           TextureDataType::kI32,
@@ -2027,7 +2025,7 @@
           ValidTextureOverload::kLoadStorageRO2dR32float,
           "textureLoad(t           : texture_storage_2d<r32float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kR32Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -2041,7 +2039,7 @@
           ValidTextureOverload::kLoadStorageRO2dRg32uint,
           "textureLoad(t           : texture_storage_2d<rg32uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRg32Uint,
           ast::TextureDimension::k2d,
           TextureDataType::kU32,
@@ -2055,7 +2053,7 @@
           ValidTextureOverload::kLoadStorageRO2dRg32sint,
           "textureLoad(t           : texture_storage_2d<rg32sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRg32Sint,
           ast::TextureDimension::k2d,
           TextureDataType::kI32,
@@ -2069,7 +2067,7 @@
           ValidTextureOverload::kLoadStorageRO2dRg32float,
           "textureLoad(t           : texture_storage_2d<rg32float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRg32Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -2083,7 +2081,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba32uint,
           "textureLoad(t           : texture_storage_2d<rgba32uint>,\n"
           "            coords      : vec2<i32>) -> vec4<u32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Uint,
           ast::TextureDimension::k2d,
           TextureDataType::kU32,
@@ -2097,7 +2095,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba32sint,
           "textureLoad(t           : texture_storage_2d<rgba32sint>,\n"
           "            coords      : vec2<i32>) -> vec4<i32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Sint,
           ast::TextureDimension::k2d,
           TextureDataType::kI32,
@@ -2111,7 +2109,7 @@
           ValidTextureOverload::kLoadStorageRO2dRgba32float,
           "textureLoad(t           : texture_storage_2d<rgba32float>,\n"
           "            coords      : vec2<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -2127,7 +2125,7 @@
           "texture_storage_2d_array<rgba32float>,\n"
           "            coords      : vec2<i32>,\n"
           "            array_index : i32) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2dArray,
           TextureDataType::kF32,
@@ -2142,7 +2140,7 @@
           ValidTextureOverload::kLoadStorageRO3dRgba32float,
           "textureLoad(t      : texture_storage_3d<rgba32float>,\n"
           "            coords : vec3<i32>) -> vec4<f32>",
-          ast::AccessControl::kRead,
+          ast::Access::kRead,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k3d,
           TextureDataType::kF32,
@@ -2157,7 +2155,7 @@
           "textureStore(t      : texture_storage_1d<rgba32float>,\n"
           "             coords : i32,\n"
           "             value  : vec4<T>)",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k1d,
           TextureDataType::kF32,
@@ -2173,7 +2171,7 @@
           "textureStore(t      : texture_storage_2d<rgba32float>,\n"
           "             coords : vec2<i32>,\n"
           "             value  : vec4<T>)",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2d,
           TextureDataType::kF32,
@@ -2190,7 +2188,7 @@
           "             coords      : vec2<i32>,\n"
           "             array_index : i32,\n"
           "             value       : vec4<T>)",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k2dArray,
           TextureDataType::kF32,
@@ -2207,7 +2205,7 @@
           "textureStore(t      : texture_storage_3d<rgba32float>,\n"
           "             coords : vec3<i32>,\n"
           "             value  : vec4<T>)",
-          ast::AccessControl::kWrite,
+          ast::Access::kWrite,
           ast::ImageFormat::kRgba32Float,
           ast::TextureDimension::k3d,
           TextureDataType::kF32,
diff --git a/src/ast/intrinsic_texture_helper_test.h b/src/ast/intrinsic_texture_helper_test.h
index 2151e47..38cc277 100644
--- a/src/ast/intrinsic_texture_helper_test.h
+++ b/src/ast/intrinsic_texture_helper_test.h
@@ -17,7 +17,7 @@
 
 #include <vector>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/program_builder.h"
 #include "src/sem/storage_texture_type.h"
 
@@ -192,7 +192,7 @@
   /// Constructor for textureLoad() with storage textures
   TextureOverloadCase(ValidTextureOverload,
                       const char*,
-                      AccessControl::Access,
+                      Access,
                       ast::ImageFormat,
                       ast::TextureDimension,
                       TextureDataType,
@@ -230,7 +230,7 @@
   ast::SamplerKind const sampler_kind = ast::SamplerKind::kSampler;
   /// The access control for the storage texture
   /// Used only when texture_kind is kStorage
-  AccessControl::Access const access_control = AccessControl::kReadWrite;
+  Access const access = Access::kReadWrite;
   /// The image format for the storage texture
   /// Used only when texture_kind is kStorage
   ast::ImageFormat const image_format = ast::ImageFormat::kNone;
diff --git a/src/ast/matrix_test.cc b/src/ast/matrix_test.cc
index 38b5f23..30b14a1 100644
--- a/src/ast/matrix_test.cc
+++ b/src/ast/matrix_test.cc
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "src/ast/matrix.h"
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/alias.h"
 #include "src/ast/array.h"
 #include "src/ast/bool.h"
diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc
index a1f4920..37a1129 100644
--- a/src/ast/module_clone_test.cc
+++ b/src/ast/module_clone_test.cc
@@ -42,15 +42,15 @@
 var<private> g0 : u32 = 20u;
 var<private> g1 : f32 = 123.0;
 [[group(0), binding(0)]] var g2 : texture_2d<f32>;
-[[group(1), binding(0)]] var g3 : [[access(read)]] texture_storage_2d<r32uint>;
-[[group(2), binding(0)]] var g4 : [[access(write)]] texture_storage_2d<rg32float>;
-[[group(3), binding(0)]] var g5 : [[access(read)]] texture_storage_2d<r32uint>;
-[[group(4), binding(0)]] var g6 : [[access(write)]] texture_storage_2d<rg32float>;
+[[group(1), binding(0)]] var g3 : texture_storage_2d<r32uint, read>;
+[[group(2), binding(0)]] var g4 : texture_storage_2d<rg32float, write>;
+[[group(3), binding(0)]] var g5 : texture_storage_2d<r32uint, read>;
+[[group(4), binding(0)]] var g6 : texture_storage_2d<rg32float, write>;
 
 var<private> g7 : vec3<f32>;
-[[group(0), binding(1)]] var<storage> g8 : [[access(write)]] S;
-[[group(1), binding(1)]] var<storage> g9 : [[access(read)]] S;
-[[group(2), binding(1)]] var<storage> g10 : [[access(read_write)]] S;
+[[group(0), binding(1)]] var<storage, write> g8 : S;
+[[group(1), binding(1)]] var<storage, read> g9 : S;
+[[group(2), binding(1)]] var<storage, read_write> g10 : S;
 
 fn f0(p0 : bool) -> f32 {
   if (p0) {
diff --git a/src/ast/multisampled_texture_test.cc b/src/ast/multisampled_texture_test.cc
index a4c55d9..5671f1e 100644
--- a/src/ast/multisampled_texture_test.cc
+++ b/src/ast/multisampled_texture_test.cc
@@ -14,7 +14,7 @@
 
 #include "src/ast/multisampled_texture.h"
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/alias.h"
 #include "src/ast/array.h"
 #include "src/ast/bool.h"
diff --git a/src/ast/pointer_test.cc b/src/ast/pointer_test.cc
index 6d0104f..d2eaa9e 100644
--- a/src/ast/pointer_test.cc
+++ b/src/ast/pointer_test.cc
@@ -13,19 +13,9 @@
 // limitations under the License.
 
 #include "src/ast/pointer.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
+
 #include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/sampled_texture_test.cc b/src/ast/sampled_texture_test.cc
index 6c94c0c..35e932e 100644
--- a/src/ast/sampled_texture_test.cc
+++ b/src/ast/sampled_texture_test.cc
@@ -14,22 +14,8 @@
 
 #include "src/ast/sampled_texture.h"
 
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/depth_texture.h"
 #include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/storage_texture.h"
-#include "src/ast/struct.h"
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/sampler_test.cc b/src/ast/sampler_test.cc
index 339e6fa..186eb48 100644
--- a/src/ast/sampler_test.cc
+++ b/src/ast/sampler_test.cc
@@ -13,19 +13,8 @@
 // limitations under the License.
 
 #include "src/ast/sampler.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/struct.h"
+
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/storage_texture.cc b/src/ast/storage_texture.cc
index f1f23d6..38207de 100644
--- a/src/ast/storage_texture.cc
+++ b/src/ast/storage_texture.cc
@@ -144,8 +144,12 @@
                                const Source& source,
                                TextureDimension dim,
                                ImageFormat format,
-                               Type* subtype)
-    : Base(program_id, source, dim), image_format_(format), subtype_(subtype) {}
+                               Type* subtype,
+                               Access access)
+    : Base(program_id, source, dim),
+      image_format_(format),
+      subtype_(subtype),
+      access_(access) {}
 
 StorageTexture::StorageTexture(StorageTexture&&) = default;
 
@@ -153,13 +157,15 @@
 
 std::string StorageTexture::type_name() const {
   std::ostringstream out;
-  out << "__storage_texture_" << dim() << "_" << image_format_;
+  out << "__storage_texture_" << dim() << "_" << image_format_ << "_"
+      << access_;
   return out.str();
 }
 
 std::string StorageTexture::FriendlyName(const SymbolTable&) const {
   std::ostringstream out;
-  out << "texture_storage_" << dim() << "<" << image_format_ << ">";
+  out << "texture_storage_" << dim() << "<" << image_format_ << ", " << access_
+      << ">";
   return out.str();
 }
 
@@ -167,7 +173,8 @@
   // Clone arguments outside of create() call to have deterministic ordering
   auto src = ctx->Clone(source());
   auto* ty = ctx->Clone(type());
-  return ctx->dst->create<StorageTexture>(src, dim(), image_format_, ty);
+  return ctx->dst->create<StorageTexture>(src, dim(), image_format(), ty,
+                                          access());
 }
 
 Type* StorageTexture::SubtypeFor(ImageFormat format, ProgramBuilder& builder) {
diff --git a/src/ast/storage_texture.h b/src/ast/storage_texture.h
index bdf16b3..9d45b95 100644
--- a/src/ast/storage_texture.h
+++ b/src/ast/storage_texture.h
@@ -17,6 +17,7 @@
 
 #include <string>
 
+#include "src/ast/access.h"
 #include "src/ast/texture.h"
 
 namespace tint {
@@ -78,21 +79,33 @@
   /// @param dim the dimensionality of the texture
   /// @param format the image format of the texture
   /// @param subtype the storage subtype. Use SubtypeFor() to calculate this.
+  /// @param access_control the access control for the texture.
   StorageTexture(ProgramID program_id,
                  const Source& source,
                  TextureDimension dim,
                  ImageFormat format,
-                 Type* subtype);
+                 Type* subtype,
+                 Access access_control);
 
   /// Move constructor
   StorageTexture(StorageTexture&&);
   ~StorageTexture() override;
 
+  /// @returns the image format
+  ImageFormat image_format() const { return image_format_; }
+
   /// @returns the storage subtype
   Type* type() const { return subtype_; }
 
-  /// @returns the image format
-  ImageFormat image_format() const { return image_format_; }
+  /// @returns the access control
+  Access access() const { return access_; }
+
+  /// @returns true if the access control is read only
+  bool is_read_only() const { return access_ == Access::kRead; }
+  /// @returns true if the access control is write only
+  bool is_write_only() const { return access_ == Access::kWrite; }
+  /// @returns true if the access control is read/write
+  bool is_read_write() const { return access_ == Access::kReadWrite; }
 
   /// @returns the name for this type
   std::string type_name() const override;
@@ -115,6 +128,7 @@
  private:
   ImageFormat const image_format_;
   Type* const subtype_;
+  Access const access_;
 };
 
 }  // namespace ast
diff --git a/src/ast/storage_texture_test.cc b/src/ast/storage_texture_test.cc
index e2ee6e6..6098ccb 100644
--- a/src/ast/storage_texture_test.cc
+++ b/src/ast/storage_texture_test.cc
@@ -13,22 +13,8 @@
 // limitations under the License.
 
 #include "src/ast/storage_texture.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/depth_texture.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampled_texture.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
+
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
@@ -38,8 +24,9 @@
 
 TEST_F(AstStorageTextureTest, IsTexture) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  Texture* ty = create<StorageTexture>(TextureDimension::k2dArray,
-                                       ImageFormat::kRgba32Float, subtype);
+  Texture* ty =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Float, subtype, Access::kRead);
   EXPECT_FALSE(ty->Is<DepthTexture>());
   EXPECT_FALSE(ty->Is<SampledTexture>());
   EXPECT_TRUE(ty->Is<StorageTexture>());
@@ -47,37 +34,42 @@
 
 TEST_F(AstStorageTextureTest, Dim) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  auto* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRgba32Float, subtype);
+  auto* s =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Float, subtype, Access::kRead);
   EXPECT_EQ(s->dim(), TextureDimension::k2dArray);
 }
 
 TEST_F(AstStorageTextureTest, Format) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  auto* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRgba32Float, subtype);
+  auto* s =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Float, subtype, Access::kRead);
   EXPECT_EQ(s->image_format(), ImageFormat::kRgba32Float);
 }
 
 TEST_F(AstStorageTextureTest, TypeName) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  auto* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRgba32Float, subtype);
-  EXPECT_EQ(s->type_name(), "__storage_texture_2d_array_rgba32float");
+  auto* s =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Float, subtype, Access::kRead);
+  EXPECT_EQ(s->type_name(), "__storage_texture_2d_array_rgba32float_read");
 }
 
 TEST_F(AstStorageTextureTest, FriendlyName) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  auto* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRgba32Float, subtype);
+  auto* s =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Float, subtype, Access::kRead);
   EXPECT_EQ(s->FriendlyName(Symbols()),
-            "texture_storage_2d_array<rgba32float>");
+            "texture_storage_2d_array<rgba32float, read>");
 }
 
 TEST_F(AstStorageTextureTest, F32) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Float, *this);
-  Type* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRgba32Float, subtype);
+  Type* s =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Float, subtype, Access::kRead);
 
   ASSERT_TRUE(s->Is<Texture>());
   ASSERT_TRUE(s->Is<StorageTexture>());
@@ -86,8 +78,9 @@
 
 TEST_F(AstStorageTextureTest, U32) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRg32Uint, *this);
-  Type* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRg32Uint, subtype);
+  Type* s =
+      create<StorageTexture>(TextureDimension::k2dArray, ImageFormat::kRg32Uint,
+                             subtype, Access::kRead);
 
   ASSERT_TRUE(s->Is<Texture>());
   ASSERT_TRUE(s->Is<StorageTexture>());
@@ -96,8 +89,9 @@
 
 TEST_F(AstStorageTextureTest, I32) {
   auto* subtype = StorageTexture::SubtypeFor(ImageFormat::kRgba32Sint, *this);
-  Type* s = create<StorageTexture>(TextureDimension::k2dArray,
-                                   ImageFormat::kRgba32Sint, subtype);
+  Type* s =
+      create<StorageTexture>(TextureDimension::k2dArray,
+                             ImageFormat::kRgba32Sint, subtype, Access::kRead);
 
   ASSERT_TRUE(s->Is<Texture>());
   ASSERT_TRUE(s->Is<StorageTexture>());
diff --git a/src/ast/u32_test.cc b/src/ast/u32_test.cc
index af57079..e13ed85 100644
--- a/src/ast/u32_test.cc
+++ b/src/ast/u32_test.cc
@@ -14,19 +14,7 @@
 
 #include "src/ast/u32.h"
 
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
-#include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/vector.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/ast/variable.cc b/src/ast/variable.cc
index d1f3395..c473fbe 100644
--- a/src/ast/variable.cc
+++ b/src/ast/variable.cc
@@ -27,6 +27,7 @@
                    const Source& source,
                    const Symbol& sym,
                    StorageClass declared_storage_class,
+                   Access declared_access,
                    const ast::Type* type,
                    bool is_const,
                    Expression* constructor,
@@ -37,7 +38,8 @@
       is_const_(is_const),
       constructor_(constructor),
       decorations_(std::move(decorations)),
-      declared_storage_class_(declared_storage_class) {
+      declared_storage_class_(declared_storage_class),
+      declared_access_(declared_access) {
   TINT_ASSERT(symbol_.IsValid());
   TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(symbol_, program_id);
   TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(constructor, program_id);
@@ -66,8 +68,9 @@
   auto* ty = ctx->Clone(type());
   auto* ctor = ctx->Clone(constructor());
   auto decos = ctx->Clone(decorations());
-  return ctx->dst->create<Variable>(src, sym, declared_storage_class(), ty,
-                                    is_const_, ctor, decos);
+  return ctx->dst->create<Variable>(src, sym, declared_storage_class(),
+                                    declared_access(), ty, is_const_, ctor,
+                                    decos);
 }
 
 void Variable::info_to_str(const sem::Info& sem,
@@ -80,6 +83,8 @@
   out << (var_sem ? var_sem->StorageClass() : declared_storage_class())
       << std::endl;
   make_indent(out, indent);
+  out << declared_access_ << std::endl;
+  make_indent(out, indent);
   out << type_->type_name() << std::endl;
 }
 
diff --git a/src/ast/variable.h b/src/ast/variable.h
index a345fcc..a618b0b 100644
--- a/src/ast/variable.h
+++ b/src/ast/variable.h
@@ -18,6 +18,7 @@
 #include <utility>
 #include <vector>
 
+#include "src/ast/access.h"
 #include "src/ast/decoration.h"
 #include "src/ast/expression.h"
 #include "src/ast/storage_class.h"
@@ -103,6 +104,7 @@
   /// @param source the variable source
   /// @param sym the variable symbol
   /// @param declared_storage_class the declared storage class
+  /// @param declared_access the declared access control
   /// @param type the declared variable type
   /// @param is_const true if the variable is const
   /// @param constructor the constructor expression
@@ -111,6 +113,7 @@
            const Source& source,
            const Symbol& sym,
            StorageClass declared_storage_class,
+           Access declared_access,
            const ast::Type* type,
            bool is_const,
            Expression* constructor,
@@ -130,6 +133,10 @@
   StorageClass declared_storage_class() const {
     return declared_storage_class_;
   }
+
+  /// @returns the declared access control
+  Access declared_access() const { return declared_access_; }
+
   /// @returns the constructor expression or nullptr if none set
   Expression* constructor() const { return constructor_; }
   /// @returns true if the variable has an constructor
@@ -184,6 +191,7 @@
   Expression* const constructor_;
   DecorationList const decorations_;
   StorageClass const declared_storage_class_;
+  Access const declared_access_;
 };
 
 /// A list of variables
diff --git a/src/ast/variable_decl_statement_test.cc b/src/ast/variable_decl_statement_test.cc
index 37776e2..e1deb63 100644
--- a/src/ast/variable_decl_statement_test.cc
+++ b/src/ast/variable_decl_statement_test.cc
@@ -76,6 +76,7 @@
   Variable{
     a
     none
+    undefined
     __f32
   }
 }
diff --git a/src/ast/variable_test.cc b/src/ast/variable_test.cc
index 5f7ce60..fd223ac 100644
--- a/src/ast/variable_test.cc
+++ b/src/ast/variable_test.cc
@@ -92,10 +92,12 @@
 }
 
 TEST_F(VariableTest, to_str) {
-  auto* v = Var("my_var", ty.f32(), StorageClass::kFunction);
+  auto* v =
+      Var("my_var", ty.f32(), StorageClass::kFunction, ast::Access::kReadWrite);
   EXPECT_EQ(str(v), R"(Variable{
   my_var
   function
+  read_write
   __f32
 }
 )");
@@ -161,7 +163,8 @@
 }
 
 TEST_F(VariableTest, Decorated_to_str) {
-  auto* var = Var("my_var", ty.f32(), StorageClass::kFunction, Expr("expr"),
+  auto* var = Var("my_var", ty.f32(), StorageClass::kFunction,
+                  ast::Access::kRead, Expr("expr"),
                   DecorationList{
                       create<BindingDecoration>(2),
                       create<GroupDecoration>(1),
@@ -174,6 +177,7 @@
   }
   my_var
   function
+  read
   __f32
   {
     Identifier[not set]{expr}
diff --git a/src/ast/vector_test.cc b/src/ast/vector_test.cc
index f419558..4f4d0ed 100644
--- a/src/ast/vector_test.cc
+++ b/src/ast/vector_test.cc
@@ -13,19 +13,9 @@
 // limitations under the License.
 
 #include "src/ast/vector.h"
-#include "src/ast/access_control.h"
-#include "src/ast/alias.h"
-#include "src/ast/array.h"
-#include "src/ast/bool.h"
-#include "src/ast/f32.h"
+
 #include "src/ast/i32.h"
-#include "src/ast/matrix.h"
-#include "src/ast/pointer.h"
-#include "src/ast/sampler.h"
-#include "src/ast/struct.h"
 #include "src/ast/test_helper.h"
-#include "src/ast/texture.h"
-#include "src/ast/u32.h"
 
 namespace tint {
 namespace ast {
diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc
index 51030f9..c0a100c 100644
--- a/src/inspector/inspector.cc
+++ b/src/inspector/inspector.cc
@@ -641,7 +641,7 @@
     auto* var = rsv.first;
     auto binding_info = rsv.second;
 
-    if (read_only != (var->AccessControl() == ast::AccessControl::kRead)) {
+    if (read_only != (var->Access() == ast::Access::kRead)) {
       continue;
     }
 
@@ -723,8 +723,7 @@
 
     auto* texture_type = var->Type()->UnwrapRef()->As<sem::StorageTexture>();
 
-    if (read_only !=
-        (texture_type->access_control() == ast::AccessControl::kRead)) {
+    if (read_only != (texture_type->access() == ast::Access::kRead)) {
       continue;
     }
 
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index dd3f534..00bc302 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -235,47 +235,12 @@
   /// Generates types appropriate for using in a storage buffer
   /// @param name name for the type
   /// @param member_types a vector of member types
-  /// @returns a function that returns an ast::AccessControl to the created
-  /// structure.
-  std::function<ast::AccessControl*()> MakeStorageBufferTypes(
+  /// @returns a function that returns the created structure.
+  std::function<ast::TypeName*()> MakeStorageBufferTypes(
       const std::string& name,
       std::vector<ast::Type*> member_types) {
     MakeStructType(name, member_types, true);
-    return [this, name] {
-      return ty.access(ast::AccessControl::kReadWrite, ty.type_name(name));
-    };
-  }
-
-  /// Generates types appropriate for using in a read-only storage buffer
-  /// @param name name for the type
-  /// @param member_types a vector of member types
-  /// @returns a function that returns an ast::AccessControl to the created
-  /// structure.
-  std::function<ast::AccessControl*()> MakeReadOnlyStorageBufferTypes(
-      const std::string& name,
-      std::vector<ast::Type*> member_types) {
-    MakeStructType(name, member_types, true);
-    return [this, name] {
-      return ty.access(ast::AccessControl::kRead, ty.type_name(name));
-    };
-  }
-
-  /// Adds a binding variable with a struct type to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param storage_class the storage class to use
-  /// @param group the binding and group to use for the uniform buffer
-  /// @param binding the binding number to use for the uniform buffer
-  void AddBinding(const std::string& name,
-                  ast::Type* type,
-                  ast::StorageClass storage_class,
-                  uint32_t group,
-                  uint32_t binding) {
-    Global(name, type, storage_class, nullptr,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
+    return [this, name] { return ty.type_name(name); };
   }
 
   /// Adds an uniform buffer variable to the program
@@ -287,19 +252,29 @@
                         ast::Type* type,
                         uint32_t group,
                         uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kUniform, group, binding);
+    Global(name, type, ast::StorageClass::kUniform,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Adds a storage buffer variable to the program
   /// @param name the name of the variable
   /// @param type the type to use
+  /// @param access the storage buffer access control
   /// @param group the binding/group to use for the storage buffer
   /// @param binding the binding number to use for the storage buffer
   void AddStorageBuffer(const std::string& name,
                         ast::Type* type,
+                        ast::Access access,
                         uint32_t group,
                         uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kStorage, group, binding);
+    Global(name, type, ast::StorageClass::kStorage, access,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Generates a function that references a specific struct variable
@@ -341,7 +316,11 @@
   /// @param group the binding/group to use for the storage buffer
   /// @param binding the binding number to use for the storage buffer
   void AddSampler(const std::string& name, uint32_t group, uint32_t binding) {
-    AddBinding(name, sampler_type(), ast::StorageClass::kNone, group, binding);
+    Global(name, sampler_type(),
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Adds a comparison sampler variable to the program
@@ -351,8 +330,11 @@
   void AddComparisonSampler(const std::string& name,
                             uint32_t group,
                             uint32_t binding) {
-    AddBinding(name, comparison_sampler_type(), ast::StorageClass::kNone, group,
-               binding);
+    Global(name, comparison_sampler_type(),
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Generates a SampledTexture appropriate for the params
@@ -396,7 +378,11 @@
                          ast::Type* type,
                          uint32_t group,
                          uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kNone, group, binding);
+    Global(name, type,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Adds a multi-sampled texture variable to the program
@@ -408,7 +394,11 @@
                               ast::Type* type,
                               uint32_t group,
                               uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kNone, group, binding);
+    Global(name, type,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   void AddGlobalVariable(const std::string& name, ast::Type* type) {
@@ -424,7 +414,11 @@
                        ast::Type* type,
                        uint32_t group,
                        uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kNone, group, binding);
+    Global(name, type,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Adds an external texture variable to the program
@@ -436,7 +430,11 @@
                           ast::Type* type,
                           uint32_t group,
                           uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kNone, group, binding);
+    Global(name, type,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Generates a function that references a specific sampler variable
@@ -571,15 +569,12 @@
   /// @param dim the texture dimension of the storage texture
   /// @param format the image format of the storage texture
   /// @param read_only should the access type be read only, otherwise write only
-  /// @returns the storage texture type, subtype & access control type
+  /// @returns the storage texture type
   ast::Type* MakeStorageTextureTypes(ast::TextureDimension dim,
                                      ast::ImageFormat format,
                                      bool read_only) {
-    auto ac =
-        read_only ? ast::AccessControl::kRead : ast::AccessControl::kWrite;
-    auto* tex = ty.storage_texture(dim, format);
-
-    return ty.access(ac, tex);
+    auto access = read_only ? ast::Access::kRead : ast::Access::kWrite;
+    return ty.storage_texture(dim, format, access);
   }
 
   /// Adds a storage texture variable to the program
@@ -591,7 +586,11 @@
                          ast::Type* type,
                          uint32_t group,
                          uint32_t binding) {
-    AddBinding(name, type, ast::StorageClass::kNone, group, binding);
+    Global(name, type,
+           ast::DecorationList{
+               create<ast::BindingDecoration>(binding),
+               create<ast::GroupDecoration>(group),
+           });
   }
 
   /// Generates a function that references a storage texture variable.
@@ -1664,11 +1663,11 @@
   MakeStructVariableReferenceBodyFunction("ub_func", "ub_var", {{0, ty.i32()}});
 
   auto sb = MakeStorageBufferTypes("sb_type", {ty.i32()});
-  AddStorageBuffer("sb_var", sb(), 1, 0);
+  AddStorageBuffer("sb_var", sb(), ast::Access::kReadWrite, 1, 0);
   MakeStructVariableReferenceBodyFunction("sb_func", "sb_var", {{0, ty.i32()}});
 
-  auto ro_sb = MakeReadOnlyStorageBufferTypes("rosb_type", {ty.i32()});
-  AddStorageBuffer("rosb_var", ro_sb(), 1, 1);
+  auto ro_sb = MakeStorageBufferTypes("rosb_type", {ty.i32()});
+  AddStorageBuffer("rosb_var", ro_sb(), ast::Access::kRead, 1, 1);
   MakeStructVariableReferenceBodyFunction("rosb_func", "rosb_var",
                                           {{0, ty.i32()}});
 
@@ -1949,7 +1948,7 @@
 
 TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) {
   auto foo_struct_type = MakeStorageBufferTypes("foo_type", {ty.i32()});
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kReadWrite, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -1978,7 +1977,7 @@
                                                                 ty.u32(),
                                                                 ty.f32(),
                                                             });
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kReadWrite, 0, 0);
 
   MakeStructVariableReferenceBodyFunction(
       "sb_func", "foo_sb", {{0, ty.i32()}, {1, ty.u32()}, {2, ty.f32()}});
@@ -2008,9 +2007,9 @@
                                                               ty.u32(),
                                                               ty.f32(),
                                                           });
-  AddStorageBuffer("sb_foo", sb_struct_type(), 0, 0);
-  AddStorageBuffer("sb_bar", sb_struct_type(), 0, 1);
-  AddStorageBuffer("sb_baz", sb_struct_type(), 2, 0);
+  AddStorageBuffer("sb_foo", sb_struct_type(), ast::Access::kReadWrite, 0, 0);
+  AddStorageBuffer("sb_bar", sb_struct_type(), ast::Access::kReadWrite, 0, 1);
+  AddStorageBuffer("sb_baz", sb_struct_type(), ast::Access::kReadWrite, 2, 0);
 
   auto AddReferenceFunc = [this](const std::string& func_name,
                                  const std::string& var_name) {
@@ -2067,7 +2066,7 @@
 TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) {
   auto foo_struct_type =
       MakeStorageBufferTypes("foo_type", {ty.i32(), ty.array<u32, 4>()});
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kReadWrite, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -2095,7 +2094,7 @@
                                                                 ty.i32(),
                                                                 ty.array<u32>(),
                                                             });
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kReadWrite, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -2120,7 +2119,7 @@
 
 TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingPadding) {
   auto foo_struct_type = MakeStorageBufferTypes("foo_type", {ty.vec3<f32>()});
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kReadWrite, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb",
                                           {{0, ty.vec3<f32>()}});
@@ -2145,8 +2144,8 @@
 }
 
 TEST_F(InspectorGetStorageBufferResourceBindingsTest, SkipReadOnly) {
-  auto foo_struct_type = MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()});
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  auto foo_struct_type = MakeStorageBufferTypes("foo_type", {ty.i32()});
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kRead, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -2163,8 +2162,8 @@
 }
 
 TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) {
-  auto foo_struct_type = MakeReadOnlyStorageBufferTypes("foo_type", {ty.i32()});
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  auto foo_struct_type = MakeStorageBufferTypes("foo_type", {ty.i32()});
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kRead, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -2189,14 +2188,14 @@
 
 TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
        MultipleStorageBuffers) {
-  auto sb_struct_type = MakeReadOnlyStorageBufferTypes("sb_type", {
-                                                                      ty.i32(),
-                                                                      ty.u32(),
-                                                                      ty.f32(),
-                                                                  });
-  AddStorageBuffer("sb_foo", sb_struct_type(), 0, 0);
-  AddStorageBuffer("sb_bar", sb_struct_type(), 0, 1);
-  AddStorageBuffer("sb_baz", sb_struct_type(), 2, 0);
+  auto sb_struct_type = MakeStorageBufferTypes("sb_type", {
+                                                              ty.i32(),
+                                                              ty.u32(),
+                                                              ty.f32(),
+                                                          });
+  AddStorageBuffer("sb_foo", sb_struct_type(), ast::Access::kRead, 0, 0);
+  AddStorageBuffer("sb_bar", sb_struct_type(), ast::Access::kRead, 0, 1);
+  AddStorageBuffer("sb_baz", sb_struct_type(), ast::Access::kRead, 2, 0);
 
   auto AddReferenceFunc = [this](const std::string& func_name,
                                  const std::string& var_name) {
@@ -2252,11 +2251,11 @@
 
 TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) {
   auto foo_struct_type =
-      MakeReadOnlyStorageBufferTypes("foo_type", {
-                                                     ty.i32(),
-                                                     ty.array<u32, 4>(),
-                                                 });
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+      MakeStorageBufferTypes("foo_type", {
+                                             ty.i32(),
+                                             ty.array<u32, 4>(),
+                                         });
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kRead, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -2281,12 +2280,11 @@
 
 TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest,
        ContainingRuntimeArray) {
-  auto foo_struct_type =
-      MakeReadOnlyStorageBufferTypes("foo_type", {
-                                                     ty.i32(),
-                                                     ty.array<u32>(),
-                                                 });
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  auto foo_struct_type = MakeStorageBufferTypes("foo_type", {
+                                                                ty.i32(),
+                                                                ty.array<u32>(),
+                                                            });
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kRead, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
@@ -2311,7 +2309,7 @@
 
 TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, SkipNonReadOnly) {
   auto foo_struct_type = MakeStorageBufferTypes("foo_type", {ty.i32()});
-  AddStorageBuffer("foo_sb", foo_struct_type(), 0, 0);
+  AddStorageBuffer("foo_sb", foo_struct_type(), ast::Access::kReadWrite, 0, 0);
 
   MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, ty.i32()}});
 
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index 4da8cbb..9cdeca7 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -286,7 +286,7 @@
 // template
 ////////////////////////////////////////////////////////////////////////////////
 using TexelFormat = ast::ImageFormat;
-using AccessControl = ast::AccessControl::Access;
+using Access = ast::Access;
 using StorageClass = ast::StorageClass;
 using ParameterUsage = sem::ParameterUsage;
 using PipelineStageSet = sem::PipelineStageSet;
@@ -577,7 +577,7 @@
   if (auto* v = ty->As<sem::StorageTexture>()) {
     if (v->dim() == dim) {
       F = Number(static_cast<uint32_t>(v->image_format()));
-      A = Number(static_cast<uint32_t>(v->access_control()));
+      A = Number(static_cast<uint32_t>(v->access()));
       return true;
     }
   }
@@ -592,7 +592,7 @@
   const sem::StorageTexture* JOIN(build_texture_storage_, suffix)(            \
       MatchState & state, Number F, Number A) {                               \
     auto format = static_cast<TexelFormat>(F.Value());                        \
-    auto access = static_cast<AccessControl>(A.Value());                      \
+    auto access = static_cast<Access>(A.Value());                             \
     auto* T = sem::StorageTexture::SubtypeFor(format, state.builder.Types()); \
     return state.builder.create<sem::StorageTexture>(dim, format, access, T); \
   }
diff --git a/src/intrinsic_table.inl b/src/intrinsic_table.inl
index 925b23b..4fca42f 100644
--- a/src/intrinsic_table.inl
+++ b/src/intrinsic_table.inl
@@ -1184,9 +1184,9 @@
 };
 
 Number ReadOrWrite::Match(MatchState&, Number number) const {
-  switch (static_cast<AccessControl>(number.Value())) {
-    case AccessControl::kRead:
-    case AccessControl::kWrite:
+  switch (static_cast<Access>(number.Value())) {
+    case Access::kRead:
+    case Access::kWrite:
       return number;
     default:
       return Number::invalid;
@@ -1212,8 +1212,8 @@
 };
 
 Number Write::Match(MatchState&, Number number) const {
-  if (number.IsAny() || number.Value() == static_cast<uint32_t>(AccessControl::kWrite)) {
-    return Number(AccessControl::kWrite);
+  if (number.IsAny() || number.Value() == static_cast<uint32_t>(Access::kWrite)) {
+    return Number(Access::kWrite);
   }
   return Number::invalid;
 }
@@ -1237,8 +1237,8 @@
 };
 
 Number Read::Match(MatchState&, Number number) const {
-  if (number.IsAny() || number.Value() == static_cast<uint32_t>(AccessControl::kRead)) {
-    return Number(AccessControl::kRead);
+  if (number.IsAny() || number.Value() == static_cast<uint32_t>(Access::kRead)) {
+    return Number(Access::kRead);
   }
   return Number::invalid;
 }
diff --git a/src/intrinsic_table_test.cc b/src/intrinsic_table_test.cc
index 4fcf033..f6b9949 100644
--- a/src/intrinsic_table_test.cc
+++ b/src/intrinsic_table_test.cc
@@ -338,7 +338,7 @@
       sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR32Float, Types());
   auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
                                           ast::ImageFormat::kR32Float,
-                                          ast::AccessControl::kRead, subtype);
+                                          ast::Access::kRead, subtype);
 
   auto* result =
       table->Lookup(IntrinsicType::kTextureLoad, {tex, vec2_i32}, Source{});
@@ -360,7 +360,7 @@
       sem::StorageTexture::SubtypeFor(ast::ImageFormat::kR32Float, Types());
   auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d,
                                           ast::ImageFormat::kR32Float,
-                                          ast::AccessControl::kWrite, subtype);
+                                          ast::Access::kWrite, subtype);
 
   auto* result = table->Lookup(IntrinsicType::kTextureStore,
                                {tex, vec2_i32, vec4_f32}, Source{});
diff --git a/src/intrinsics.def b/src/intrinsics.def
index d63d72a..955b33f 100644
--- a/src/intrinsics.def
+++ b/src/intrinsics.def
@@ -34,7 +34,7 @@
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl/#memory-access-mode
-enum access_control {
+enum access {
   read
   write
   read_write
@@ -89,10 +89,10 @@
 type texture_depth_2d_array
 type texture_depth_cube
 type texture_depth_cube_array
-type texture_storage_1d<F: texel_format, A: access_control>
-type texture_storage_2d<F: texel_format, A: access_control>
-type texture_storage_2d_array<F: texel_format, A: access_control>
-type texture_storage_3d<F: texel_format, A: access_control>
+type texture_storage_1d<F: texel_format, A: access>
+type texture_storage_2d<F: texel_format, A: access>
+type texture_storage_2d_array<F: texel_format, A: access>
+type texture_storage_3d<F: texel_format, A: access>
 type texture_external
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -245,7 +245,7 @@
 //     - Single parameter of vector type with open-number size N and element  //
 //       open-type T                                                          //
 //                                                                            //
-//   fn F<A: access_control>(texture_storage_1d<f32_texel_format, A>)         //
+//   fn F<A: access>(texture_storage_1d<f32_texel_format, A>)                 //
 //     - Single parameter of texture_storage_1d type with open-number         //
 //       access-control C, and of a texel format that is listed in            //
 //       f32_texel_format                                                     //
diff --git a/src/program_builder.h b/src/program_builder.h
index 5319a13..371ab4d 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -101,11 +101,13 @@
     ~VarOptionals();
 
     ast::StorageClass storage = ast::StorageClass::kNone;
+    ast::Access access = ast::Access::kUndefined;
     ast::Expression* constructor = nullptr;
     ast::DecorationList decorations = {};
 
    private:
     void Set(ast::StorageClass sc) { storage = sc; }
+    void Set(ast::Access ac) { access = ac; }
     void Set(ast::Expression* c) { constructor = c; }
     void Set(const ast::DecorationList& l) { decorations = l; }
 
@@ -708,29 +710,6 @@
       return builder->create<ast::Alias>(source, sym, type);
     }
 
-    /// Creates an access control qualifier type
-    /// @param access the access control
-    /// @param type the inner type
-    /// @returns the access control qualifier type
-    ast::AccessControl* access(ast::AccessControl::Access access,
-                               const ast::Type* type) const {
-      type = MaybeCreateTypename(type);
-      return type ? builder->create<ast::AccessControl>(access, type) : nullptr;
-    }
-
-    /// Creates an access control qualifier type
-    /// @param source the Source of the node
-    /// @param access the access control
-    /// @param type the inner type
-    /// @returns the access control qualifier type
-    ast::AccessControl* access(const Source& source,
-                               ast::AccessControl::Access access,
-                               const ast::Type* type) const {
-      type = MaybeCreateTypename(type);
-      return type ? builder->create<ast::AccessControl>(source, access, type)
-                  : nullptr;
-    }
-
     /// @param type the type of the pointer
     /// @param storage_class the storage class of the pointer
     /// @return the pointer to `type` with the given ast::StorageClass
@@ -823,23 +802,28 @@
 
     /// @param dims the dimensionality of the texture
     /// @param format the image format of the texture
+    /// @param access the access control of the texture
     /// @returns the storage texture
     ast::StorageTexture* storage_texture(ast::TextureDimension dims,
-                                         ast::ImageFormat format) const {
+                                         ast::ImageFormat format,
+                                         ast::Access access) const {
       auto* subtype = ast::StorageTexture::SubtypeFor(format, *builder);
-      return builder->create<ast::StorageTexture>(dims, format, subtype);
+      return builder->create<ast::StorageTexture>(dims, format, subtype,
+                                                  access);
     }
 
     /// @param source the Source of the node
     /// @param dims the dimensionality of the texture
     /// @param format the image format of the texture
+    /// @param access the access control of the texture
     /// @returns the storage texture
     ast::StorageTexture* storage_texture(const Source& source,
                                          ast::TextureDimension dims,
-                                         ast::ImageFormat format) const {
+                                         ast::ImageFormat format,
+                                         ast::Access access) const {
       auto* subtype = ast::StorageTexture::SubtypeFor(format, *builder);
-      return builder->create<ast::StorageTexture>(source, dims, format,
-                                                  subtype);
+      return builder->create<ast::StorageTexture>(source, dims, format, subtype,
+                                                  access);
     }
 
     /// @returns the external texture
@@ -1216,6 +1200,7 @@
   /// @param optional the optional variable settings.
   /// Can be any of the following, in any order:
   ///   * ast::StorageClass   - specifies the variable storage class
+  ///   * ast::Access         - specifies the variable's access control
   ///   * ast::Expression*    - specifies the variable's initializer expression
   ///   * ast::DecorationList - specifies the variable's decorations
   /// Note that repeated arguments of the same type will use the last argument's
@@ -1229,7 +1214,7 @@
     type = ty.MaybeCreateTypename(type);
     VarOptionals opts(std::forward<OPTIONAL>(optional)...);
     return create<ast::Variable>(Sym(std::forward<NAME>(name)), opts.storage,
-                                 type, false, opts.constructor,
+                                 opts.access, type, false, opts.constructor,
                                  std::move(opts.decorations));
   }
 
@@ -1239,6 +1224,7 @@
   /// @param optional the optional variable settings.
   /// Can be any of the following, in any order:
   ///   * ast::StorageClass   - specifies the variable storage class
+  ///   * ast::Access         - specifies the variable's access control
   ///   * ast::Expression*    - specifies the variable's initializer expression
   ///   * ast::DecorationList - specifies the variable's decorations
   /// Note that repeated arguments of the same type will use the last argument's
@@ -1252,8 +1238,8 @@
     type = ty.MaybeCreateTypename(type);
     VarOptionals opts(std::forward<OPTIONAL>(optional)...);
     return create<ast::Variable>(source, Sym(std::forward<NAME>(name)),
-                                 opts.storage, type, false, opts.constructor,
-                                 std::move(opts.decorations));
+                                 opts.storage, opts.access, type, false,
+                                 opts.constructor, std::move(opts.decorations));
   }
 
   /// @param name the variable name
@@ -1267,9 +1253,9 @@
                        ast::Expression* constructor,
                        ast::DecorationList decorations = {}) {
     type = ty.MaybeCreateTypename(type);
-    return create<ast::Variable>(Sym(std::forward<NAME>(name)),
-                                 ast::StorageClass::kNone, type, true,
-                                 constructor, decorations);
+    return create<ast::Variable>(
+        Sym(std::forward<NAME>(name)), ast::StorageClass::kNone,
+        ast::Access::kUndefined, type, true, constructor, decorations);
   }
 
   /// @param source the variable source
@@ -1285,9 +1271,9 @@
                        ast::Expression* constructor,
                        ast::DecorationList decorations = {}) {
     type = ty.MaybeCreateTypename(type);
-    return create<ast::Variable>(source, Sym(std::forward<NAME>(name)),
-                                 ast::StorageClass::kNone, type, true,
-                                 constructor, decorations);
+    return create<ast::Variable>(
+        source, Sym(std::forward<NAME>(name)), ast::StorageClass::kNone,
+        ast::Access::kUndefined, type, true, constructor, decorations);
   }
 
   /// @param name the parameter name
@@ -1299,9 +1285,9 @@
                        ast::Type* type,
                        ast::DecorationList decorations = {}) {
     type = ty.MaybeCreateTypename(type);
-    return create<ast::Variable>(Sym(std::forward<NAME>(name)),
-                                 ast::StorageClass::kNone, type, true, nullptr,
-                                 decorations);
+    return create<ast::Variable>(
+        Sym(std::forward<NAME>(name)), ast::StorageClass::kNone,
+        ast::Access::kUndefined, type, true, nullptr, decorations);
   }
 
   /// @param source the parameter source
@@ -1315,9 +1301,9 @@
                        ast::Type* type,
                        ast::DecorationList decorations = {}) {
     type = ty.MaybeCreateTypename(type);
-    return create<ast::Variable>(source, Sym(std::forward<NAME>(name)),
-                                 ast::StorageClass::kNone, type, true, nullptr,
-                                 decorations);
+    return create<ast::Variable>(
+        source, Sym(std::forward<NAME>(name)), ast::StorageClass::kNone,
+        ast::Access::kUndefined, type, true, nullptr, decorations);
   }
 
   /// @param name the variable name
@@ -1325,6 +1311,7 @@
   /// @param optional the optional variable settings.
   /// Can be any of the following, in any order:
   ///   * ast::StorageClass   - specifies the variable storage class
+  ///   * ast::Access         - specifies the variable's access control
   ///   * ast::Expression*    - specifies the variable's initializer expression
   ///   * ast::DecorationList - specifies the variable's decorations
   /// Note that repeated arguments of the same type will use the last argument's
@@ -1347,6 +1334,7 @@
   /// @param optional the optional variable settings.
   /// Can be any of the following, in any order:
   ///   * ast::StorageClass   - specifies the variable storage class
+  ///   * ast::Access         - specifies the variable's access control
   ///   * ast::Expression*    - specifies the variable's initializer expression
   ///   * ast::DecorationList - specifies the variable's decorations
   /// Note that repeated arguments of the same type will use the last argument's
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 97d1f18..da4f579 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -962,7 +962,7 @@
     const auto param_name = namer_.MakeDerivedName(var_name + "_param");
     const auto param_sym = builder_.Symbols().Register(param_name);
     auto* param = create<ast::Variable>(
-        source, param_sym, ast::StorageClass::kNone,
+        source, param_sym, ast::StorageClass::kNone, ast::Access::kUndefined,
         forced_store_type->Build(builder_), true /* is const */,
         nullptr /* no constructor */, param_decos);
     decl.params.push_back(param);
@@ -2542,9 +2542,7 @@
   if (!guard_name.empty()) {
     // Declare the guard variable just before the "if", initialized to true.
     auto* guard_var =
-        create<ast::Variable>(Source{}, builder_.Symbols().Register(guard_name),
-                              ast::StorageClass::kNone, builder_.ty.bool_(),
-                              false, MakeTrue(Source{}), ast::DecorationList{});
+        builder_.Var(guard_name, builder_.ty.bool_(), MakeTrue(Source{}));
     auto* guard_decl = create<ast::VariableDeclStatement>(Source{}, guard_var);
     AddStatement(guard_decl);
   }
@@ -3102,11 +3100,9 @@
     TINT_ASSERT(def_inst);
     const auto phi_var_name = GetDefInfo(id)->phi_var;
     TINT_ASSERT(!phi_var_name.empty());
-    auto* var = create<ast::Variable>(
-        Source{}, builder_.Symbols().Register(phi_var_name),
-        ast::StorageClass::kNone,
-        parser_impl_.ConvertType(def_inst->type_id())->Build(builder_), false,
-        nullptr, ast::DecorationList{});
+    auto* var = builder_.Var(
+        phi_var_name,
+        parser_impl_.ConvertType(def_inst->type_id())->Build(builder_));
     AddStatement(create<ast::VariableDeclStatement>(Source{}, var));
   }
 
@@ -5401,7 +5397,8 @@
 
   auto* temp_var = create<ast::Variable>(
       Source{}, registered_temp_name, ast::StorageClass::kNone,
-      ast_type->Build(builder_), false, src_vector.expr, ast::DecorationList{});
+      ast::Access::kUndefined, ast_type->Build(builder_), false,
+      src_vector.expr, ast::DecorationList{});
   AddStatement(create<ast::VariableDeclStatement>(Source{}, temp_var));
 
   auto* lhs = create<ast::ArrayAccessorExpression>(
@@ -5429,7 +5426,7 @@
   // like this avoids constantly reloading the value many times.
   //
   // This technique is a combination of:
-  // - making a temporary variable and constant declaration, like  what we do
+  // - making a temporary variable and constant declaration, like what we do
   //   for VectorInsertDynamic, and
   // - building up an access-chain like access like for CompositeExtract, but
   //   on the left-hand side of the assignment.
@@ -5445,10 +5442,10 @@
   auto temp_name = namer_.MakeDerivedName(result_name);
   auto registered_temp_name = builder_.Symbols().Register(temp_name);
 
-  auto* temp_var =
-      create<ast::Variable>(Source{}, registered_temp_name,
-                            ast::StorageClass::kNone, ast_type->Build(builder_),
-                            false, src_composite.expr, ast::DecorationList{});
+  auto* temp_var = create<ast::Variable>(
+      Source{}, registered_temp_name, ast::StorageClass::kNone,
+      ast::Access::kUndefined, ast_type->Build(builder_), false,
+      src_composite.expr, ast::DecorationList{});
   AddStatement(create<ast::VariableDeclStatement>(Source{}, temp_var));
 
   TypedExpression seed_expr{ast_type, create<ast::IdentifierExpression>(
diff --git a/src/reader/spirv/function_arithmetic_test.cc b/src/reader/spirv/function_arithmetic_test.cc
index e3a0f25..4897015 100644
--- a/src/reader/spirv/function_arithmetic_test.cc
+++ b/src/reader/spirv/function_arithmetic_test.cc
@@ -159,6 +159,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       UnaryOp[not set]{
@@ -187,6 +188,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       UnaryOp[not set]{
@@ -217,6 +219,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -247,6 +250,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -279,6 +283,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       UnaryOp[not set]{
@@ -311,6 +316,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       UnaryOp[not set]{
@@ -345,6 +351,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -379,6 +386,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -415,6 +423,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       UnaryOp[not set]{
@@ -443,6 +452,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       UnaryOp[not set]{
@@ -498,6 +508,7 @@
   ss << R"(VariableConst{
     x_1
     none
+    undefined
     )"
      << GetParam().ast_type << "\n    {\n      Binary[not set]{"
      << "\n        " << GetParam().ast_lhs << "\n        " << GetParam().ast_op
@@ -543,6 +554,7 @@
   ss << R"(VariableConst{
     x_1
     none
+    undefined
     )"
      << GetParam().expected;
   auto got = ToString(p->builder(), fe.ast_body());
@@ -1021,6 +1033,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1056,6 +1069,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1166,6 +1180,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1201,6 +1216,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1253,6 +1269,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __vec_2__f32
     {
       Binary[not set]{
@@ -1282,6 +1299,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __mat_2_2__f32
     {
       Binary[not set]{
@@ -1311,6 +1329,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __vec_2__f32
     {
       Binary[not set]{
@@ -1340,6 +1359,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __vec_2__f32
     {
       Binary[not set]{
@@ -1369,6 +1389,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __mat_2_2__f32
     {
       Binary[not set]{
@@ -1398,6 +1419,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_3
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -1432,6 +1454,7 @@
   EXPECT_THAT(got, HasSubstr(R"(VariableConst{
     x_3
     none
+    undefined
     __mat_3_2__f32
     {
       TypeConstructor[not set]{
@@ -1560,6 +1583,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_2
     none
+    undefined
     )" + arg.ast_type + R"(
     {
       Call[not set]{
@@ -1610,6 +1634,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __mat_2_2__f32
     {
       Call[not set]{
@@ -1648,6 +1673,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __mat_2_3__f32
     {
       Call[not set]{
@@ -1683,6 +1709,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __mat_3_2__f32
     {
       Call[not set]{
diff --git a/src/reader/spirv/function_bit_test.cc b/src/reader/spirv/function_bit_test.cc
index 4632c19..a8011cb 100644
--- a/src/reader/spirv/function_bit_test.cc
+++ b/src/reader/spirv/function_bit_test.cc
@@ -163,6 +163,7 @@
   ss << R"(VariableConst{
     x_1
     none
+    undefined
     )"
      << GetParam().ast_type << "\n    {\n      Binary[not set]{"
      << "\n        " << GetParam().ast_lhs << "\n        " << GetParam().ast_op
@@ -208,6 +209,7 @@
   ss << R"(VariableConst{
     x_1
     none
+    undefined
     )"
      << GetParam().expected;
   auto got = ToString(p->builder(), fe.ast_body());
@@ -1130,6 +1132,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       UnaryOp[not set]{
@@ -1156,6 +1159,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Bitcast[not set]<__i32>{
@@ -1184,6 +1188,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1212,6 +1217,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       UnaryOp[not set]{
@@ -1238,6 +1244,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       UnaryOp[not set]{
@@ -1268,6 +1275,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Bitcast[not set]<__vec_2__i32>{
@@ -1300,6 +1308,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1331,6 +1340,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       UnaryOp[not set]{
@@ -1386,6 +1396,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -1414,6 +1425,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1444,6 +1456,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Bitcast[not set]<__i32>{
@@ -1474,6 +1487,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Call[not set]{
@@ -1502,6 +1516,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Call[not set]{
@@ -1530,6 +1545,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1560,6 +1576,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Bitcast[not set]<__vec_2__i32>{
@@ -1590,6 +1607,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Call[not set]{
@@ -1618,6 +1636,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -1674,6 +1693,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Call[not set]{
@@ -1702,6 +1722,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Call[not set]{
@@ -1758,6 +1779,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Call[not set]{
diff --git a/src/reader/spirv/function_call_test.cc b/src/reader/spirv/function_call_test.cc
index c9ec4fc..638cb45 100644
--- a/src/reader/spirv/function_call_test.cc
+++ b/src/reader/spirv/function_call_test.cc
@@ -92,6 +92,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -148,6 +149,7 @@
   Variable{
     x_10
     none
+    undefined
     __u32
   }
 }
@@ -155,6 +157,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -218,11 +221,13 @@
     VariableConst{
       x_51
       none
+      undefined
       __u32
     }
     VariableConst{
       x_52
       none
+      undefined
       __u32
     }
   )
@@ -244,6 +249,7 @@
       VariableConst{
         x_1
         none
+        undefined
         __u32
         {
           Call[not set]{
diff --git a/src/reader/spirv/function_cfg_test.cc b/src/reader/spirv/function_cfg_test.cc
index 044e7ae..a369ac6 100644
--- a/src/reader/spirv/function_cfg_test.cc
+++ b/src/reader/spirv/function_cfg_test.cc
@@ -7910,6 +7910,7 @@
   Variable{
     guard10
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{true}
@@ -8022,6 +8023,7 @@
   Variable{
     guard10
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{true}
@@ -8149,6 +8151,7 @@
   Variable{
     guard10
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{true}
diff --git a/src/reader/spirv/function_composite_test.cc b/src/reader/spirv/function_composite_test.cc
index 4fd0f07..392d021 100644
--- a/src/reader/spirv/function_composite_test.cc
+++ b/src/reader/spirv/function_composite_test.cc
@@ -92,6 +92,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -106,6 +107,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__i32
     {
       TypeConstructor[not set]{
@@ -120,6 +122,7 @@
   VariableConst{
     x_3
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -148,6 +151,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __mat_2_3__f32
     {
       TypeConstructor[not set]{
@@ -188,6 +192,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __array__u32_5
     {
       TypeConstructor[not set]{
@@ -218,6 +223,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -252,6 +258,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       MemberAccessor[not set]{
@@ -302,6 +309,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__f32
     {
       ArrayAccessor[not set]{
@@ -352,6 +360,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __f32
     {
       MemberAccessor[not set]{
@@ -385,6 +394,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       ArrayAccessor[not set]{
@@ -436,6 +446,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __i32
     {
       MemberAccessor[not set]{
@@ -478,6 +489,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       MemberAccessor[not set]{
@@ -491,6 +503,7 @@
   VariableConst{
     x_4
     none
+    undefined
     __u32
     {
       MemberAccessor[not set]{
@@ -544,6 +557,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __f32
     {
       MemberAccessor[not set]{
@@ -582,6 +596,7 @@
   Variable{
     x_1_1
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -603,6 +618,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Identifier[not set]{x_1_1}
@@ -648,6 +664,7 @@
   Variable{
     x_2_1
     none
+    undefined
     __mat_2_3__f32
     {
       Identifier[not set]{x_1}
@@ -669,6 +686,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __mat_2_3__f32
     {
       Identifier[not set]{x_2_1}
@@ -718,6 +736,7 @@
   Variable{
     x_2_1
     none
+    undefined
     __mat_2_3__f32
     {
       Identifier[not set]{x_1}
@@ -739,6 +758,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __mat_2_3__f32
     {
       Identifier[not set]{x_2_1}
@@ -768,6 +788,7 @@
   Variable{
     x_2_1
     none
+    undefined
     __array__u32_5
     {
       Identifier[not set]{x_1}
@@ -785,6 +806,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __array__u32_5
     {
       Identifier[not set]{x_2_1}
@@ -835,6 +857,7 @@
   Variable{
     x_35
     none
+    undefined
     __type_name_S
   }
 }
@@ -842,6 +865,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __type_name_S
     {
       Identifier[not set]{x_35}
@@ -852,6 +876,7 @@
   Variable{
     x_2_1
     none
+    undefined
     __type_name_S
     {
       Identifier[not set]{x_1}
@@ -869,6 +894,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __type_name_S
     {
       Identifier[not set]{x_2_1}
@@ -909,6 +935,7 @@
   Variable{
     x_40
     none
+    undefined
     __type_name_S_2
   }
 }
@@ -916,6 +943,7 @@
   Variable{
     x_41
     none
+    undefined
     __type_name_S_2
   }
 }
@@ -923,6 +951,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __type_name_S_2
     {
       Identifier[not set]{x_40}
@@ -933,6 +962,7 @@
   Variable{
     x_2_1
     none
+    undefined
     __type_name_S_1
     {
       Identifier[not set]{x_1}
@@ -950,6 +980,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __type_name_S_1
     {
       Identifier[not set]{x_2_1}
@@ -960,6 +991,7 @@
   VariableConst{
     x_3
     none
+    undefined
     __type_name_S_2
     {
       Identifier[not set]{x_41}
@@ -970,6 +1002,7 @@
   Variable{
     x_4_1
     none
+    undefined
     __type_name_S_2
     {
       Identifier[not set]{x_3}
@@ -987,6 +1020,7 @@
   VariableConst{
     x_4
     none
+    undefined
     __type_name_S_2
     {
       Identifier[not set]{x_4_1}
@@ -998,6 +1032,7 @@
   Variable{
     x_4_1
     none
+    undefined
     __type_name_S_2
     {
       Identifier[not set]{x_3}
@@ -1015,6 +1050,7 @@
   VariableConst{
     x_4
     none
+    undefined
     __type_name_S_2
     {
       Identifier[not set]{x_4_1}
@@ -1066,6 +1102,7 @@
   Variable{
     x_37
     none
+    undefined
     __type_name_S_1
   }
 }
@@ -1073,6 +1110,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __type_name_S_1
     {
       Identifier[not set]{x_37}
@@ -1083,6 +1121,7 @@
   Variable{
     x_2_1
     none
+    undefined
     __type_name_S_1
     {
       Identifier[not set]{x_1}
@@ -1109,6 +1148,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __type_name_S_1
     {
       Identifier[not set]{x_2_1}
@@ -1137,6 +1177,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{3u}
@@ -1147,6 +1188,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_1}
@@ -1176,6 +1218,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __ptr_function__u32
     {
       UnaryOp[not set]{
@@ -1189,6 +1232,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __ptr_function__u32
     {
       Identifier[not set]{x_1}
@@ -1218,6 +1262,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __vec_4__u32
     {
       TypeConstructor[not set]{
@@ -1260,6 +1305,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __vec_4__u32
     {
       TypeConstructor[not set]{
@@ -1318,6 +1364,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -1370,6 +1417,7 @@
   EXPECT_THAT(got, HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __u32
     {
       ArrayAccessor[not set]{
@@ -1400,6 +1448,7 @@
   EXPECT_THAT(got, HasSubstr(R"(VariableConst{
     x_10
     none
+    undefined
     __u32
     {
       ArrayAccessor[not set]{
@@ -1435,6 +1484,7 @@
   Variable{
     x_10_1
     none
+    undefined
     __vec_2__u32
     {
       Identifier[not set]{x_1}
@@ -1452,6 +1502,7 @@
   VariableConst{
     x_10
     none
+    undefined
     __vec_2__u32
     {
       Identifier[not set]{x_10_1}
diff --git a/src/reader/spirv/function_conversion_test.cc b/src/reader/spirv/function_conversion_test.cc
index 6d77b27..cb3d381 100644
--- a/src/reader/spirv/function_conversion_test.cc
+++ b/src/reader/spirv/function_conversion_test.cc
@@ -86,6 +86,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -111,6 +112,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Bitcast[not set]<__vec_2__f32>{
@@ -239,6 +241,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       TypeConstructor[not set]{
@@ -265,6 +268,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       TypeConstructor[not set]{
@@ -293,6 +297,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -319,6 +324,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -382,6 +388,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       TypeConstructor[not set]{
@@ -410,6 +417,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       TypeConstructor[not set]{
@@ -436,6 +444,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -464,6 +473,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -526,6 +536,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       TypeConstructor[not set]{
@@ -552,6 +563,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -580,6 +592,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       TypeConstructor[not set]{
@@ -606,6 +619,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -686,6 +700,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       TypeConstructor[not set]{
@@ -728,6 +743,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -778,6 +794,7 @@
   EXPECT_THAT(ToString(p->builder(), fe.ast_body()), HasSubstr(R"(VariableConst{
         x_82
         none
+        undefined
         __u32
         {
           TypeConstructor[not set]{
diff --git a/src/reader/spirv/function_decl_test.cc b/src/reader/spirv/function_decl_test.cc
index cb38e8f..41cc2f0 100644
--- a/src/reader/spirv/function_decl_test.cc
+++ b/src/reader/spirv/function_decl_test.cc
@@ -118,16 +118,19 @@
     VariableConst{
       a
       none
+      undefined
       __u32
     }
     VariableConst{
       b
       none
+      undefined
       __f32
     }
     VariableConst{
       c
       none
+      undefined
       __i32
     }
   )
@@ -162,16 +165,19 @@
     VariableConst{
       x_14
       none
+      undefined
       __u32
     }
     VariableConst{
       x_15
       none
+      undefined
       __f32
     }
     VariableConst{
       x_16
       none
+      undefined
       __i32
     }
   )
diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc
index bb5756c..3e5b5e4 100644
--- a/src/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/reader/spirv/function_glsl_std_450_test.cc
@@ -188,6 +188,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -219,6 +220,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -250,6 +252,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -282,6 +285,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -314,6 +318,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -345,6 +350,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Call[not set]{
@@ -376,6 +382,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -408,6 +415,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Call[not set]{
@@ -440,6 +448,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -474,6 +483,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Call[not set]{
@@ -507,6 +517,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -540,6 +551,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Call[not set]{
@@ -572,6 +584,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -605,6 +618,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Call[not set]{
@@ -638,6 +652,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_3__f32
     {
       Call[not set]{
@@ -744,6 +759,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Call[not set]{
@@ -776,6 +792,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Call[not set]{
@@ -808,6 +825,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Call[not set]{
@@ -841,6 +859,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Call[not set]{
@@ -874,6 +893,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Call[not set]{
@@ -908,6 +928,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__i32
     {
       Call[not set]{
@@ -954,6 +975,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -987,6 +1009,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Call[not set]{
@@ -1019,6 +1042,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -1053,6 +1077,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Call[not set]{
@@ -1099,6 +1124,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{1.000000}
@@ -1123,6 +1149,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__f32
     {
       Call[not set]{
@@ -1152,6 +1179,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_3__f32
     {
       Call[not set]{
@@ -1181,6 +1209,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_4__f32
     {
       Call[not set]{
@@ -1213,6 +1242,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1232,6 +1262,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1265,6 +1296,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1287,6 +1319,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1323,6 +1356,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1345,6 +1379,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1381,6 +1416,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Bitcast[not set]<__u32>{
@@ -1404,6 +1440,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__u32
     {
       Bitcast[not set]<__vec_2__u32>{
@@ -1441,6 +1478,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Bitcast[not set]<__i32>{
@@ -1463,6 +1501,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__i32
     {
       Bitcast[not set]<__vec_2__i32>{
@@ -1499,6 +1538,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Bitcast[not set]<__i32>{
@@ -1521,6 +1561,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__i32
     {
       Bitcast[not set]<__vec_2__i32>{
@@ -1557,6 +1598,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __i32
     {
       Bitcast[not set]<__i32>{
@@ -1580,6 +1622,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __vec_2__i32
     {
       Bitcast[not set]<__vec_2__i32>{
@@ -1633,6 +1676,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -1677,6 +1721,7 @@
   VariableConst{
     x_1
     none
+    undefined
     )" + std::string(param.vec_size == 2 ? "__vec_2__f32" : "__vec_4__f32") +
                               R"(
     {
diff --git a/src/reader/spirv/function_logical_test.cc b/src/reader/spirv/function_logical_test.cc
index e1f3f44..180e115 100644
--- a/src/reader/spirv/function_logical_test.cc
+++ b/src/reader/spirv/function_logical_test.cc
@@ -210,6 +210,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -236,6 +237,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -290,6 +292,7 @@
   ss << R"(VariableConst{
     x_1
     none
+    undefined
     )"
      << GetParam().ast_type << "\n    {\n      Binary[not set]{"
      << "\n        " << GetParam().ast_lhs << "\n        " << GetParam().ast_op
@@ -728,6 +731,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -758,6 +762,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -796,6 +801,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -826,6 +832,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -864,6 +871,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -894,6 +902,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -932,6 +941,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -962,6 +972,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -1000,6 +1011,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -1030,6 +1042,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -1068,6 +1081,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -1098,6 +1112,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       UnaryOp[not set]{
@@ -1139,6 +1154,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       Call[not set]{
@@ -1171,6 +1187,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
@@ -1203,6 +1220,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __f32
     {
       Call[not set]{
@@ -1238,6 +1256,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Call[not set]{
@@ -1284,6 +1303,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__u32
     {
       Call[not set]{
@@ -1328,6 +1348,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       Call[not set]{
@@ -1362,6 +1383,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       Call[not set]{
@@ -1396,6 +1418,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       Call[not set]{
@@ -1426,6 +1449,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       Call[not set]{
@@ -1460,6 +1484,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __bool
     {
       Call[not set]{
@@ -1490,6 +1515,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __vec_2__bool
     {
       Call[not set]{
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
index f44846a..0ef8989 100644
--- a/src/reader/spirv/function_memory_test.cc
+++ b/src/reader/spirv/function_memory_test.cc
@@ -180,6 +180,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_1}
@@ -210,6 +211,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_1}
@@ -220,6 +222,7 @@
   VariableConst{
     x_3
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_1}
@@ -252,6 +255,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_1}
@@ -873,7 +877,8 @@
     }
     myvar
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   })"));
 }
 
@@ -1017,6 +1022,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __ptr_storage__u32
     {
       UnaryOp[not set]{
@@ -1078,6 +1084,7 @@
   Variable{
     x_2
     none
+    undefined
     __ptr_storage__u32
   }
 }
@@ -1176,6 +1183,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       Call[not set]{
diff --git a/src/reader/spirv/function_misc_test.cc b/src/reader/spirv/function_misc_test.cc
index c22e4d1..4ecea55 100644
--- a/src/reader/spirv/function_misc_test.cc
+++ b/src/reader/spirv/function_misc_test.cc
@@ -78,6 +78,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -88,6 +89,7 @@
   VariableConst{
     x_12
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{0u}
@@ -98,6 +100,7 @@
   VariableConst{
     x_13
     none
+    undefined
     __i32
     {
       ScalarConstructor[not set]{0}
@@ -108,6 +111,7 @@
   VariableConst{
     x_14
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{0.000000}
@@ -142,6 +146,7 @@
   VariableConst{
     x_14
     none
+    undefined
     __vec_2__bool
     {
       TypeConstructor[not set]{
@@ -156,6 +161,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -170,6 +176,7 @@
   VariableConst{
     x_12
     none
+    undefined
     __vec_2__i32
     {
       TypeConstructor[not set]{
@@ -184,6 +191,7 @@
   VariableConst{
     x_13
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -221,6 +229,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -231,6 +240,7 @@
   VariableConst{
     x_12
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{0u}
@@ -241,6 +251,7 @@
   VariableConst{
     x_13
     none
+    undefined
     __i32
     {
       ScalarConstructor[not set]{0}
@@ -251,6 +262,7 @@
   VariableConst{
     x_14
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{0.000000}
@@ -282,6 +294,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -296,6 +309,7 @@
   VariableConst{
     x_12
     none
+    undefined
     __vec_2__i32
     {
       TypeConstructor[not set]{
@@ -310,6 +324,7 @@
   VariableConst{
     x_13
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -343,6 +358,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __mat_2_2__f32
     {
       TypeConstructor[not set]{
@@ -385,6 +401,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __array__u32_2
     {
       TypeConstructor[not set]{
@@ -418,6 +435,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -541,6 +559,7 @@
       VariableConst{
         x_81
         none
+        undefined
         __f32
         {
           Binary[not set]{
diff --git a/src/reader/spirv/function_var_test.cc b/src/reader/spirv/function_var_test.cc
index dc474a0..c2ba7ec 100644
--- a/src/reader/spirv/function_var_test.cc
+++ b/src/reader/spirv/function_var_test.cc
@@ -108,6 +108,7 @@
   Variable{
     x_1
     none
+    undefined
     __u32
   }
 }
@@ -115,6 +116,7 @@
   Variable{
     x_2
     none
+    undefined
     __u32
   }
 }
@@ -122,6 +124,7 @@
   Variable{
     x_3
     none
+    undefined
     __u32
   }
 }
@@ -147,6 +150,7 @@
   Variable{
     a
     none
+    undefined
     __u32
   }
 }
@@ -154,6 +158,7 @@
   Variable{
     b
     none
+    undefined
     __u32
   }
 }
@@ -161,6 +166,7 @@
   Variable{
     c
     none
+    undefined
     __u32
   }
 }
@@ -186,6 +192,7 @@
   Variable{
     a
     none
+    undefined
     __u32
   }
 }
@@ -193,6 +200,7 @@
   Variable{
     b
     none
+    undefined
     __i32
   }
 }
@@ -200,6 +208,7 @@
   Variable{
     c
     none
+    undefined
     __f32
   }
 }
@@ -227,6 +236,7 @@
   Variable{
     a
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{true}
@@ -237,6 +247,7 @@
   Variable{
     b
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -247,6 +258,7 @@
   Variable{
     c
     none
+    undefined
     __i32
     {
       ScalarConstructor[not set]{-1}
@@ -257,6 +269,7 @@
   Variable{
     d
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{1u}
@@ -267,6 +280,7 @@
   Variable{
     e
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{1.500000}
@@ -301,6 +315,7 @@
   Variable{
     a
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -311,6 +326,7 @@
   Variable{
     b
     none
+    undefined
     __i32
     {
       ScalarConstructor[not set]{0}
@@ -321,6 +337,7 @@
   Variable{
     c
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{0u}
@@ -331,6 +348,7 @@
   Variable{
     d
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{0.000000}
@@ -361,6 +379,7 @@
   Variable{
     x_200
     none
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -400,6 +419,7 @@
   Variable{
     x_200
     none
+    undefined
     __mat_2_3__f32
     {
       TypeConstructor[not set]{
@@ -447,6 +467,7 @@
   Variable{
     x_200
     none
+    undefined
     __array__u32_2
     {
       TypeConstructor[not set]{
@@ -486,6 +507,7 @@
   Variable{
     x_200
     none
+    undefined
     __type_name_Arr
     {
       TypeConstructor[not set]{
@@ -521,6 +543,7 @@
   Variable{
     x_200
     none
+    undefined
     __array__u32_2
     {
       TypeConstructor[not set]{
@@ -561,6 +584,7 @@
   Variable{
     x_200
     none
+    undefined
     __type_name_Arr
     {
       TypeConstructor[not set]{
@@ -596,6 +620,7 @@
   Variable{
     x_200
     none
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -636,6 +661,7 @@
   Variable{
     x_200
     none
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -682,6 +708,7 @@
   Variable{
     x_25
     none
+    undefined
     __u32
   }
 }
@@ -730,6 +757,7 @@
   Variable{
     x_25
     none
+    undefined
     __u32
   }
 }
@@ -737,6 +765,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       Binary[not set]{
@@ -802,6 +831,7 @@
   Variable{
     x_25
     none
+    undefined
     __u32
   }
 }
@@ -809,6 +839,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       Binary[not set]{
@@ -906,6 +937,7 @@
     Variable{
       x_2
       none
+      undefined
       __u32
     }
   }
@@ -1019,6 +1051,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{1u}
@@ -1036,6 +1069,7 @@
   VariableConst{
     x_3
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_1}
@@ -1109,6 +1143,7 @@
       VariableConst{
         x_1
         none
+        undefined
         __u32
         {
           ScalarConstructor[not set]{1u}
@@ -1126,6 +1161,7 @@
       VariableConst{
         x_3
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -1196,6 +1232,7 @@
       VariableConst{
         x_1
         none
+        undefined
         __u32
         {
           ScalarConstructor[not set]{1u}
@@ -1215,6 +1252,7 @@
       VariableConst{
         x_3
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -1274,6 +1312,7 @@
   VariableConst{
     x_1
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{1u}
@@ -1284,6 +1323,7 @@
   VariableConst{
     x_2
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_1}
@@ -1352,6 +1392,7 @@
     Variable{
       x_2_phi
       none
+      undefined
       __u32
     }
   }
@@ -1359,6 +1400,7 @@
     Variable{
       x_3_phi
       none
+      undefined
       __u32
     }
   }
@@ -1366,6 +1408,7 @@
     VariableConst{
       x_101
       none
+      undefined
       __bool
       {
         Identifier[not set]{x_7}
@@ -1376,6 +1419,7 @@
     VariableConst{
       x_102
       none
+      undefined
       __bool
       {
         Identifier[not set]{x_8}
@@ -1403,6 +1447,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_2_phi}
@@ -1413,6 +1458,7 @@
       VariableConst{
         x_3
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_3_phi}
@@ -1499,6 +1545,7 @@
     Variable{
       x_2_phi
       none
+      undefined
       __u32
     }
   }
@@ -1506,6 +1553,7 @@
     Variable{
       x_3_phi
       none
+      undefined
       __u32
     }
   }
@@ -1513,6 +1561,7 @@
     VariableConst{
       x_101
       none
+      undefined
       __bool
       {
         Identifier[not set]{x_7}
@@ -1523,6 +1572,7 @@
     VariableConst{
       x_102
       none
+      undefined
       __bool
       {
         Identifier[not set]{x_8}
@@ -1550,6 +1600,7 @@
       Variable{
         x_4
         none
+        undefined
         __u32
       }
     }
@@ -1557,6 +1608,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_2_phi}
@@ -1567,6 +1619,7 @@
       VariableConst{
         x_3
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_3_phi}
@@ -1660,6 +1713,7 @@
   VariableConst{
     x_101
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_17}
@@ -1671,6 +1725,7 @@
     Variable{
       x_2_phi
       none
+      undefined
       __u32
     }
   }
@@ -1678,6 +1733,7 @@
     Variable{
       x_5_phi
       none
+      undefined
       __u32
     }
   }
@@ -1694,6 +1750,7 @@
       Variable{
         x_7
         none
+        undefined
         __u32
       }
     }
@@ -1701,6 +1758,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_2_phi}
@@ -1711,6 +1769,7 @@
       VariableConst{
         x_5
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_5_phi}
@@ -1721,6 +1780,7 @@
       VariableConst{
         x_4
         none
+        undefined
         __u32
         {
           Binary[not set]{
@@ -1735,6 +1795,7 @@
       VariableConst{
         x_6
         none
+        undefined
         __u32
         {
           Binary[not set]{
@@ -1832,6 +1893,7 @@
   VariableConst{
     x_101
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_7}
@@ -1842,6 +1904,7 @@
   VariableConst{
     x_102
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_8}
@@ -1853,6 +1916,7 @@
     Variable{
       x_2_phi
       none
+      undefined
       __u32
     }
   }
@@ -1890,6 +1954,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_2_phi}
@@ -1958,6 +2023,7 @@
   VariableConst{
     x_101
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_7}
@@ -1968,6 +2034,7 @@
   VariableConst{
     x_102
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_8}
@@ -1979,6 +2046,7 @@
     Variable{
       x_2_phi
       none
+      undefined
       __u32
     }
   }
@@ -2017,6 +2085,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_2_phi}
@@ -2084,6 +2153,7 @@
   Variable{
     x_35_phi
     none
+    undefined
     __u32
   }
 }
@@ -2124,6 +2194,7 @@
   VariableConst{
     x_35
     none
+    undefined
     __u32
     {
       Identifier[not set]{x_35_phi}
@@ -2174,6 +2245,7 @@
   Variable{
     x_101_phi
     none
+    undefined
     __bool
   }
 }
@@ -2181,6 +2253,7 @@
   VariableConst{
     x_11
     none
+    undefined
     __bool
     {
       Binary[not set]{
@@ -2195,6 +2268,7 @@
   VariableConst{
     x_12
     none
+    undefined
     __bool
     {
       UnaryOp[not set]{
@@ -2223,6 +2297,7 @@
   VariableConst{
     x_101
     none
+    undefined
     __bool
     {
       Identifier[not set]{x_101_phi}
@@ -2291,6 +2366,7 @@
       Variable{
         x_81_phi_1
         none
+        undefined
         __f32
       }
     }
@@ -2298,6 +2374,7 @@
       VariableConst{
         x_81
         none
+        undefined
         __f32
         {
           Identifier[not set]{x_81_phi_1}
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index a573634..9f6a865 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -1354,6 +1354,7 @@
     return nullptr;
   }
 
+  ast::Access access = ast::Access::kUndefined;
   if (sc == ast::StorageClass::kStorage) {
     bool read_only = false;
     if (auto* tn = storage_type->As<Named>()) {
@@ -1361,9 +1362,7 @@
     }
 
     // Apply the access(read) or access(read_write) modifier.
-    auto access =
-        read_only ? ast::AccessControl::kRead : ast::AccessControl::kReadWrite;
-    storage_type = ty_.AccessControl(storage_type, access);
+    access = read_only ? ast::Access::kRead : ast::Access::kReadWrite;
   }
 
   // Handle variables (textures and samplers) are always in the handle
@@ -1387,7 +1386,7 @@
   // `var` declarations will have a resolved type of ref<storage>, but at the
   // AST level both `var` and `let` are declared with the same type.
   return create<ast::Variable>(Source{}, builder_.Symbols().Register(name), sc,
-                               storage_type->Build(builder_), is_const,
+                               access, storage_type->Build(builder_), is_const,
                                constructor, decorations);
 }
 
@@ -1501,7 +1500,7 @@
   }
 
   auto source = GetSourceForInst(inst);
-  auto* ast_type = original_ast_type->UnwrapAliasAndAccess();
+  auto* ast_type = original_ast_type->UnwrapAlias();
 
   // TODO(dneto): Note: NullConstant for int, uint, float map to a regular 0.
   // So canonicalization should map that way too.
@@ -1577,7 +1576,7 @@
   }
 
   auto* original_type = type;
-  type = type->UnwrapAliasAndAccess();
+  type = type->UnwrapAlias();
 
   if (type->Is<Bool>()) {
     return create<ast::ScalarConstructorExpression>(
@@ -2123,15 +2122,13 @@
         ast_store_type = ty_.SampledTexture(dim, ast_sampled_component_type);
       }
     } else {
-      const auto access = usage.IsStorageReadTexture()
-                              ? ast::AccessControl::kRead
-                              : ast::AccessControl::kWrite;
+      const auto access = usage.IsStorageReadTexture() ? ast::Access::kRead
+                                                       : ast::Access::kWrite;
       const auto format = enum_converter_.ToImageFormat(image_type->format());
       if (format == ast::ImageFormat::kNone) {
         return nullptr;
       }
-      ast_store_type =
-          ty_.AccessControl(ty_.StorageTexture(dim, format), access);
+      ast_store_type = ty_.StorageTexture(dim, format, access);
     }
   } else {
     Fail() << "unsupported: UniformConstant variable is not a recognized "
diff --git a/src/reader/spirv/parser_impl_function_decl_test.cc b/src/reader/spirv/parser_impl_function_decl_test.cc
index 5b2e753..a78ce0a 100644
--- a/src/reader/spirv/parser_impl_function_decl_test.cc
+++ b/src/reader/spirv/parser_impl_function_decl_test.cc
@@ -220,6 +220,7 @@
       VariableConst{
         leaf_result
         none
+        undefined
         __u32
         {
           Call[not set]{
@@ -243,6 +244,7 @@
       VariableConst{
         branch_result
         none
+        undefined
         __u32
         {
           Call[not set]{
@@ -307,16 +309,19 @@
     VariableConst{
       a
       none
+      undefined
       __u32
     }
     VariableConst{
       b
       none
+      undefined
       __f32
     }
     VariableConst{
       c
       none
+      undefined
       __i32
     }
   )
@@ -347,16 +352,19 @@
     VariableConst{
       x_14
       none
+      undefined
       __u32
     }
     VariableConst{
       x_15
       none
+      undefined
       __f32
     }
     VariableConst{
       x_16
       none
+      undefined
       __i32
     }
   )
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
index 8569bb5..116b7bc 100644
--- a/src/reader/spirv/parser_impl_handle_test.cc
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -1270,6 +1270,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   })"}));
 
@@ -1288,6 +1289,7 @@
     }
     x_10
     none
+    undefined
     __sampled_texture_1d__f32
   })"},
                              DeclUnderspecifiedHandleCase{R"(
@@ -1304,7 +1306,8 @@
     }
     x_10
     none
-    __access_control_read_only__storage_texture_1d_rg32float
+    undefined
+    __storage_texture_1d_rg32float_read
   })"},
                              DeclUnderspecifiedHandleCase{R"(
          OpDecorate %10 NonReadable
@@ -1320,7 +1323,8 @@
     }
     x_10
     none
-    __access_control_write_only__storage_texture_1d_rg32float
+    undefined
+    __storage_texture_1d_rg32float_write
   })"}));
 
 // Test handle declaration or error, when there is an image access.
@@ -1453,6 +1457,7 @@
     }
     x_20
     none
+    undefined
     __multisampled_texture_2d__f32
   }
 )"},
@@ -1639,6 +1644,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1648,6 +1654,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -1672,6 +1679,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1681,6 +1689,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -1716,6 +1725,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1725,6 +1735,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
             R"(
@@ -1755,6 +1766,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1764,6 +1776,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
             R"(
@@ -1803,6 +1816,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1812,6 +1826,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -1837,6 +1852,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1846,6 +1862,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -1882,6 +1899,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1891,6 +1909,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -1923,6 +1942,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1932,6 +1952,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -1965,6 +1986,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -1974,6 +1996,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -2026,6 +2049,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2035,6 +2059,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   }
   Variable{
@@ -2044,6 +2069,7 @@
     }
     x_30
     none
+    undefined
     __sampler_comparison
   })",
                         R"(
@@ -2051,6 +2077,7 @@
       VariableConst{
         x_200
         none
+        undefined
         __vec_4__f32
         {
           TypeConstructor[not set]{
@@ -2074,6 +2101,7 @@
       VariableConst{
         x_210
         none
+        undefined
         __f32
         {
           Call[not set]{
@@ -2105,6 +2133,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2114,6 +2143,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
                         R"(
@@ -2138,6 +2168,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2147,6 +2178,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d_array
   })",
                         R"(
@@ -2182,6 +2214,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2191,6 +2224,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
             R"(
@@ -2221,6 +2255,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2230,6 +2265,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d_array
   })",
             R"(
@@ -2276,6 +2312,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2285,6 +2322,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
                         R"(
@@ -2309,6 +2347,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2318,6 +2357,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d_array
   })",
                         R"(
@@ -2353,6 +2393,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2362,6 +2403,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
                         R"(
@@ -2392,6 +2434,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2401,6 +2444,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d_array
   })",
                         R"(
@@ -2440,6 +2484,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2449,6 +2494,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_cube
   })",
                         R"(
@@ -2473,6 +2519,7 @@
     }
     x_10
     none
+    undefined
     __sampler_comparison
   }
   Variable{
@@ -2482,6 +2529,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_cube_array
   })",
                         R"(
@@ -2522,6 +2570,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2531,6 +2580,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -2556,6 +2606,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2565,6 +2616,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -2601,6 +2653,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2610,6 +2663,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -2642,6 +2696,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2651,6 +2706,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -2685,6 +2741,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2694,6 +2751,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -2739,6 +2797,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2748,6 +2807,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -2774,6 +2834,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2783,6 +2844,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -2820,6 +2882,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2829,6 +2892,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -2861,6 +2925,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2870,6 +2935,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                         R"(
@@ -2905,6 +2971,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2914,6 +2981,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -2957,6 +3025,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -2966,6 +3035,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
                         R"(
@@ -3020,6 +3090,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -3029,6 +3100,7 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(
@@ -3053,6 +3125,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -3062,6 +3135,7 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
          R"(
@@ -3165,7 +3239,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_write
   })",
                               R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3195,7 +3270,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_r32float
+    undefined
+    __storage_texture_2d_r32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3221,7 +3297,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_r32float
+    undefined
+    __storage_texture_2d_r32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3246,7 +3323,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_r32float
+    undefined
+    __storage_texture_2d_r32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3270,7 +3348,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_r32float
+    undefined
+    __storage_texture_2d_r32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3290,7 +3369,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rg32float
+    undefined
+    __storage_texture_2d_rg32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3315,7 +3395,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rg32float
+    undefined
+    __storage_texture_2d_rg32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3339,7 +3420,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rg32float
+    undefined
+    __storage_texture_2d_rg32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3360,7 +3442,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3456,7 +3539,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32uint
+    undefined
+    __storage_texture_2d_rgba32uint_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3476,7 +3560,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32sint
+    undefined
+    __storage_texture_2d_rgba32sint_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3506,7 +3591,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_write
   })"},
         // Sampled type is float, texel is unsigned int
         {"%int 2D 0 0 0 2 Rgba32f", "OpImageWrite %im %vi12 %vu1234",
@@ -3521,7 +3607,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_write
   })"},
         // Sampled type is unsigned int, texel is float
         {"%uint 2D 0 0 0 2 Rgba32ui", "OpImageWrite %im %vi12 %vf1234",
@@ -3536,7 +3623,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32uint
+    undefined
+    __storage_texture_2d_rgba32uint_write
   })"},
         // Sampled type is signed int, texel is float
         {"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vi12 %vf1234",
@@ -3551,7 +3639,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32sint
+    undefined
+    __storage_texture_2d_rgba32sint_write
   })"}}));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -3573,7 +3662,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32uint
+    undefined
+    __storage_texture_2d_rgba32uint_write
   })"},
         // Sampled type is signed int, texel is unsigned int
         {"%int 2D 0 0 0 2 Rgba32i", "OpImageWrite %im %vi12 %vu1234",
@@ -3588,7 +3678,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_rgba32sint
+    undefined
+    __storage_texture_2d_rgba32sint_write
   })"}}));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -3607,7 +3698,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_r32uint
+    undefined
+    __storage_texture_2d_r32uint_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3632,7 +3724,8 @@
     }
     x_20
     none
-    __access_control_write_only__storage_texture_2d_r32sint
+    undefined
+    __storage_texture_2d_r32sint_write
   })",
          R"(Call[not set]{
       Identifier[not set]{textureStore}
@@ -3661,12 +3754,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_read
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -3694,12 +3789,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -3723,12 +3820,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -3752,12 +3851,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           TypeConstructor[not set]{
@@ -3787,12 +3888,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           TypeConstructor[not set]{
@@ -3830,12 +3933,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           TypeConstructor[not set]{
@@ -3876,12 +3981,14 @@
     }
     x_20
     none
+    undefined
     __multisampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -3909,12 +4016,14 @@
     }
     x_20
     none
+    undefined
     __multisampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -3959,12 +4068,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -3987,12 +4098,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__u32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__u32
         {
           Call[not set]{
@@ -4020,12 +4133,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__i32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__i32
         {
           Call[not set]{
@@ -4057,12 +4172,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -4084,12 +4201,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_2d_rgba32uint
+    undefined
+    __storage_texture_2d_rgba32uint_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__u32
         {
           Call[not set]{
@@ -4117,12 +4236,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_2d_rgba32sint
+    undefined
+    __storage_texture_2d_rgba32sint_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__i32
         {
           Call[not set]{
@@ -4158,6 +4279,7 @@
     }
     x_10
     none
+    undefined
     __sampler_sampler
   }
   Variable{
@@ -4167,12 +4289,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_4__f32
         {
           Call[not set]{
@@ -4206,12 +4330,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_1d_rgba32float
+    undefined
+    __storage_texture_1d_rgba32float_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -4238,12 +4364,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_2d_rgba32float
+    undefined
+    __storage_texture_2d_rgba32float_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_2__i32
         {
           TypeConstructor[not set]{
@@ -4270,12 +4398,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_3d_rgba32float
+    undefined
+    __storage_texture_3d_rgba32float_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4300,12 +4430,14 @@
     }
     x_20
     none
+    undefined
     __multisampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_2__i32
         {
           TypeConstructor[not set]{
@@ -4341,12 +4473,14 @@
     }
     x_20
     none
-    __access_control_read_only__storage_texture_2d_array_rgba32float
+    undefined
+    __storage_texture_2d_array_rgba32float_read
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4388,12 +4522,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_1d__f32
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -4420,12 +4556,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_2__i32
         {
           TypeConstructor[not set]{
@@ -4452,12 +4590,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_3d__f32
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4484,12 +4624,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_cube__f32
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_2__i32
         {
           TypeConstructor[not set]{
@@ -4519,12 +4661,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_2__i32
         {
           TypeConstructor[not set]{
@@ -4551,12 +4695,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_cube
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_2__i32
         {
           TypeConstructor[not set]{
@@ -4596,12 +4742,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4640,12 +4788,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_cube_array__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4681,12 +4831,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d_array
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4723,12 +4875,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_cube_array
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __vec_3__i32
         {
           TypeConstructor[not set]{
@@ -4771,12 +4925,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_1d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -4814,12 +4970,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_1d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __u32
         {
           TypeConstructor[not set]{
@@ -4854,12 +5012,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -4881,12 +5041,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d_array__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -4908,12 +5070,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_3d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -4935,12 +5099,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_cube__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -4962,12 +5128,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_cube_array__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -4989,12 +5157,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -5016,12 +5186,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_2d_array
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -5043,12 +5215,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_cube
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -5070,12 +5244,14 @@
     }
     x_20
     none
+    undefined
     __depth_texture_cube_array
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -5102,12 +5278,14 @@
     }
     x_20
     none
+    undefined
     __sampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __u32
         {
           TypeConstructor[not set]{
@@ -5136,12 +5314,14 @@
     }
     x_20
     none
+    undefined
     __multisampled_texture_2d__f32
   })",
                               R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __i32
         {
           Call[not set]{
@@ -5172,12 +5352,14 @@
     }
     x_20
     none
+    undefined
     __multisampled_texture_2d__f32
   })",
          R"(VariableDeclStatement{
       VariableConst{
         x_99
         none
+        undefined
         __u32
         {
           TypeConstructor[not set]{
@@ -6231,6 +6413,7 @@
   Variable{
     var_1
     none
+    undefined
     __vec_4__f32
   }
 }
@@ -6238,6 +6421,7 @@
   VariableConst{
     x_22
     none
+    undefined
     __vec_4__f32
     {
       Call[not set]{
@@ -6259,6 +6443,7 @@
   VariableConst{
     x_26
     none
+    undefined
     __vec_4__f32
     {
       Call[not set]{
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index 7899896..6109957 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -196,6 +196,7 @@
   Variable{
     x_52
     workgroup
+    undefined
     __f32
   })"));
 }
@@ -217,6 +218,7 @@
   Variable{
     the_counter
     workgroup
+    undefined
     __f32
   })"));
 }
@@ -238,6 +240,7 @@
   Variable{
     my_own_private_idaho
     private
+    undefined
     __f32
   })"));
 }
@@ -267,6 +270,7 @@
     }
     x_52
     in
+    undefined
     __u32
   })"));
 }
@@ -322,6 +326,7 @@
     }
     gl_Position
     out
+    undefined
     __vec_4__f32
   })"))
       << module_str;
@@ -547,6 +552,7 @@
     }
     gl_Position
     out
+    undefined
     __vec_4__f32
   }
   Function main -> __void
@@ -601,6 +607,7 @@
   Variable{
     x_900
     private
+    undefined
     __f32
   }
   Variable{
@@ -609,6 +616,7 @@
     }
     gl_Position
     out
+    undefined
     __vec_4__f32
   }
   Function main -> __void
@@ -672,6 +680,7 @@
     }
     gl_Position
     out
+    undefined
     __vec_4__f32
   }
   Function main -> __void
@@ -770,6 +779,7 @@
   Variable{
     x_900
     private
+    undefined
     __f32
   }
   Function main -> __void
@@ -961,6 +971,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_1
     private
+    undefined
     __bool
     {
       ScalarConstructor[not set]{true}
@@ -969,6 +980,7 @@
   Variable{
     x_2
     private
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -977,6 +989,7 @@
   Variable{
     x_3
     private
+    undefined
     __i32
     {
       ScalarConstructor[not set]{-1}
@@ -985,6 +998,7 @@
   Variable{
     x_4
     private
+    undefined
     __u32
     {
       ScalarConstructor[not set]{1u}
@@ -993,6 +1007,7 @@
   Variable{
     x_5
     private
+    undefined
     __f32
     {
       ScalarConstructor[not set]{1.500000}
@@ -1018,6 +1033,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_1
     private
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -1026,6 +1042,7 @@
   Variable{
     x_2
     private
+    undefined
     __i32
     {
       ScalarConstructor[not set]{0}
@@ -1034,6 +1051,7 @@
   Variable{
     x_3
     private
+    undefined
     __u32
     {
       ScalarConstructor[not set]{0u}
@@ -1042,6 +1060,7 @@
   Variable{
     x_4
     private
+    undefined
     __f32
     {
       ScalarConstructor[not set]{0.000000}
@@ -1067,6 +1086,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_1
     private
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -1075,6 +1095,7 @@
   Variable{
     x_2
     private
+    undefined
     __i32
     {
       ScalarConstructor[not set]{0}
@@ -1083,6 +1104,7 @@
   Variable{
     x_3
     private
+    undefined
     __u32
     {
       ScalarConstructor[not set]{0u}
@@ -1091,6 +1113,7 @@
   Variable{
     x_4
     private
+    undefined
     __f32
     {
       ScalarConstructor[not set]{0.000000}
@@ -1114,6 +1137,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -1137,6 +1161,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__bool
     {
       TypeConstructor[not set]{
@@ -1160,6 +1185,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__bool
     {
       TypeConstructor[not set]{
@@ -1186,6 +1212,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -1209,6 +1236,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__u32
     {
       TypeConstructor[not set]{
@@ -1235,6 +1263,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__i32
     {
       TypeConstructor[not set]{
@@ -1258,6 +1287,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__i32
     {
       TypeConstructor[not set]{
@@ -1284,6 +1314,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -1307,6 +1338,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __vec_2__f32
     {
       TypeConstructor[not set]{
@@ -1339,6 +1371,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __mat_2_3__f32
     {
       TypeConstructor[not set]{
@@ -1375,6 +1408,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __mat_2_3__f32
     {
       TypeConstructor[not set]{
@@ -1411,6 +1445,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __mat_2_3__f32
     {
       TypeConstructor[not set]{
@@ -1451,6 +1486,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __array__u32_2
     {
       TypeConstructor[not set]{
@@ -1474,6 +1510,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __array__u32_2
     {
       TypeConstructor[not set]{
@@ -1497,6 +1534,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __array__u32_2
     {
       TypeConstructor[not set]{
@@ -1525,6 +1563,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -1554,6 +1593,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -1585,6 +1625,7 @@
   EXPECT_THAT(module_str, HasSubstr(R"(Variable{
     x_200
     private
+    undefined
     __type_name_S
     {
       TypeConstructor[not set]{
@@ -1623,6 +1664,7 @@
     }
     myvar
     in
+    undefined
     __u32
   })"))
       << module_str;
@@ -1674,7 +1716,8 @@
     }
     x_1
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   })"))
       << module_str;
 }
@@ -1727,7 +1770,8 @@
     }
     x_1
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   })"))
       << module_str;
 }
@@ -1784,7 +1828,8 @@
   Variable{
     x_1
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   }
 )")) << module_str;
 }
@@ -1817,7 +1862,8 @@
   Variable{
     myvar
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   }
 })")) << module_str;
 }
@@ -1849,7 +1895,8 @@
   Variable{
     myvar
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   }
 })")) << module_str;
 }
@@ -1905,7 +1952,8 @@
   Variable{
     x_1
     storage
-    __access_control_read_only__type_name_S
+    read
+    __type_name_S
   }
 })")) << module_str;
 }
@@ -1937,7 +1985,8 @@
   Variable{
     x_1
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   }
 })")) << module_str;
 }
@@ -1972,7 +2021,8 @@
   Variable{
     x_1
     storage
-    __access_control_read_write__type_name_S
+    read_write
+    __type_name_S
   }
 })")) << module_str;
 }
@@ -1996,6 +2046,7 @@
     }
     myconst
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{true}
@@ -2023,6 +2074,7 @@
     }
     myconst
     none
+    undefined
     __bool
     {
       ScalarConstructor[not set]{false}
@@ -2050,6 +2102,7 @@
     }
     myconst
     none
+    undefined
     __u32
     {
       ScalarConstructor[not set]{42u}
@@ -2077,6 +2130,7 @@
     }
     myconst
     none
+    undefined
     __i32
     {
       ScalarConstructor[not set]{42}
@@ -2104,6 +2158,7 @@
     }
     myconst
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{2.500000}
@@ -2129,6 +2184,7 @@
   VariableConst{
     myconst
     none
+    undefined
     __f32
     {
       ScalarConstructor[not set]{2.500000}
@@ -2212,6 +2268,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2221,6 +2278,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -2254,6 +2312,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2263,6 +2322,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -2296,6 +2356,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2305,6 +2366,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -2363,6 +2425,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2372,6 +2435,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -2402,6 +2466,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2411,6 +2476,7 @@
       VariableConst{
         x_11
         none
+        undefined
         __ptr_in__u32
         {
           UnaryOp[not set]{
@@ -2424,6 +2490,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           UnaryOp[not set]{
@@ -2457,6 +2524,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2466,6 +2534,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -2574,6 +2643,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2583,6 +2653,7 @@
       VariableConst{
         x_3
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -2616,6 +2687,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2625,6 +2697,7 @@
       VariableConst{
         x_4
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -2658,6 +2731,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2667,6 +2741,7 @@
       VariableConst{
         x_4
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -2699,6 +2774,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2708,6 +2784,7 @@
       VariableConst{
         x_3
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -2744,6 +2821,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2753,6 +2831,7 @@
       VariableConst{
         x_4
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -2789,6 +2868,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -2798,6 +2878,7 @@
       VariableConst{
         x_4
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -2852,6 +2933,7 @@
     }
     x_1
     out
+    undefined
     __u32
   })"));
 
@@ -2888,6 +2970,7 @@
     }
     x_1
     out
+    undefined
     __u32
   })"));
 
@@ -2924,6 +3007,7 @@
     }
     x_1
     out
+    undefined
     __u32
   })"));
 
@@ -2959,6 +3043,7 @@
     }
     x_1
     out
+    undefined
     __u32
   })"));
 
@@ -3001,6 +3086,7 @@
     }
     x_1
     out
+    undefined
     __u32
   })"));
 
@@ -3043,6 +3129,7 @@
     }
     x_1
     out
+    undefined
     __u32
   })"));
 
@@ -3100,6 +3187,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3109,6 +3197,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -3142,6 +3231,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3151,6 +3241,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -3184,6 +3275,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3193,6 +3285,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -3251,6 +3344,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3260,6 +3354,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -3290,6 +3385,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3299,6 +3395,7 @@
       VariableConst{
         x_11
         none
+        undefined
         __ptr_in__u32
         {
           UnaryOp[not set]{
@@ -3312,6 +3409,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           UnaryOp[not set]{
@@ -3345,6 +3443,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3354,6 +3453,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -3427,6 +3527,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3436,6 +3537,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -3469,6 +3571,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3478,6 +3581,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -3511,6 +3615,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3520,6 +3625,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __i32
         {
           TypeConstructor[not set]{
@@ -3576,6 +3682,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3585,6 +3692,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -3615,6 +3723,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3624,6 +3733,7 @@
       VariableConst{
         x_11
         none
+        undefined
         __ptr_in__u32
         {
           UnaryOp[not set]{
@@ -3637,6 +3747,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           UnaryOp[not set]{
@@ -3670,6 +3781,7 @@
     }
     x_1
     in
+    undefined
     __u32
   })"));
 
@@ -3679,6 +3791,7 @@
       VariableConst{
         x_2
         none
+        undefined
         __u32
         {
           Identifier[not set]{x_1}
@@ -3832,6 +3945,7 @@
       R"(Variable{
     x_1
     private
+    undefined
     __u32
   }
 )";
@@ -3856,6 +3970,7 @@
       R"(Variable{
     x_1
     private
+    undefined
     __u32
   }
 )";
@@ -3903,21 +4018,25 @@
   Variable{
     x_1
     private
+    undefined
     __u32
   }
   Variable{
     x_2
     private
+    undefined
     __u32
   }
   Variable{
     x_3
     private
+    undefined
     __u32
   }
   Variable{
     x_4
     private
+    undefined
     __u32
   }
   Function main_1 -> __void
@@ -3934,6 +4053,7 @@
       }
       x_1_param
       none
+      undefined
       __u32
     }
     VariableConst{
@@ -3942,6 +4062,7 @@
       }
       x_3_param
       none
+      undefined
       __u32
     }
   )
diff --git a/src/reader/spirv/parser_type.cc b/src/reader/spirv/parser_type.cc
index a2db682..4f46b7d 100644
--- a/src/reader/spirv/parser_type.cc
+++ b/src/reader/spirv/parser_type.cc
@@ -33,7 +33,6 @@
 TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Vector);
 TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Matrix);
 TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Array);
-TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::AccessControl);
 TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Sampler);
 TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Texture);
 TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::DepthTexture);
@@ -79,12 +78,6 @@
   }
 };
 
-struct AccessControlHasher {
-  size_t operator()(const AccessControl& t) const {
-    return utils::Hash(t.type, t.access);
-  }
-};
-
 struct MultisampledTextureHasher {
   size_t operator()(const MultisampledTexture& t) const {
     return utils::Hash(t.dims, t.type);
@@ -99,7 +92,7 @@
 
 struct StorageTextureHasher {
   size_t operator()(const StorageTexture& t) const {
-    return utils::Hash(t.dims, t.format);
+    return utils::Hash(t.dims, t.format, t.access);
   }
 };
 }  // namespace
@@ -124,10 +117,6 @@
   return a.type == b.type && a.size == b.size && a.stride == b.stride;
 }
 
-static bool operator==(const AccessControl& a, const AccessControl& b) {
-  return a.type == b.type && a.access == b.access;
-}
-
 static bool operator==(const MultisampledTexture& a,
                        const MultisampledTexture& b) {
   return a.dims == b.dims && a.type == b.type;
@@ -200,14 +189,6 @@
   return b.ty.array(type->Build(b), size, stride);
 }
 
-AccessControl::AccessControl(const Type* t, ast::AccessControl::Access a)
-    : type(t), access(a) {}
-AccessControl::AccessControl(const AccessControl&) = default;
-
-ast::Type* AccessControl::Build(ProgramBuilder& b) const {
-  return b.ty.access(access, type->Build(b));
-}
-
 Sampler::Sampler(ast::SamplerKind k) : kind(k) {}
 Sampler::Sampler(const Sampler&) = default;
 
@@ -241,12 +222,14 @@
   return b.ty.sampled_texture(dims, type->Build(b));
 }
 
-StorageTexture::StorageTexture(ast::TextureDimension d, ast::ImageFormat f)
-    : Base(d), format(f) {}
+StorageTexture::StorageTexture(ast::TextureDimension d,
+                               ast::ImageFormat f,
+                               ast::Access a)
+    : Base(d), format(f), access(a) {}
 StorageTexture::StorageTexture(const StorageTexture&) = default;
 
 ast::Type* StorageTexture::Build(ProgramBuilder& b) const {
-  return b.ty.storage_texture(dims, format);
+  return b.ty.storage_texture(dims, format, access);
 }
 
 Named::Named(Symbol n) : name(n) {}
@@ -296,11 +279,6 @@
       matrices_;
   /// Map of Array to the returned Array type instance
   std::unordered_map<spirv::Array, const spirv::Array*, ArrayHasher> arrays_;
-  /// Map of AccessControl to the returned AccessControl type instance
-  std::unordered_map<spirv::AccessControl,
-                     const spirv::AccessControl*,
-                     AccessControlHasher>
-      access_controls_;
   /// Map of type name to returned Alias instance
   std::unordered_map<Symbol, const spirv::Alias*> aliases_;
   /// Map of type name to returned Struct instance
@@ -352,27 +330,11 @@
   return type;
 }
 
-const Type* Type::UnwrapAliasAndAccess() const {
-  auto* type = this;
-  while (true) {
-    if (auto* alias = type->As<Alias>()) {
-      type = alias->type;
-    } else if (auto* access = type->As<AccessControl>()) {
-      type = access->type;
-    } else {
-      break;
-    }
-  }
-  return type;
-}
-
 const Type* Type::UnwrapAll() const {
   auto* type = this;
   while (true) {
     if (auto* alias = type->As<Alias>()) {
       type = alias->type;
-    } else if (auto* access = type->As<AccessControl>()) {
-      type = access->type;
     } else if (auto* ptr = type->As<Pointer>()) {
       type = ptr->type;
     } else {
@@ -500,14 +462,6 @@
       [&] { return state->allocator_.Create<spirv::Array>(el, size, stride); });
 }
 
-const spirv::AccessControl* TypeManager::AccessControl(
-    const Type* ty,
-    ast::AccessControl::Access ac) {
-  return utils::GetOrCreate(
-      state->access_controls_, spirv::AccessControl(ty, ac),
-      [&] { return state->allocator_.Create<spirv::AccessControl>(ty, ac); });
-}
-
 const spirv::Alias* TypeManager::Alias(Symbol name, const Type* ty) {
   return utils::GetOrCreate(state->aliases_, name, [&] {
     return state->allocator_.Create<spirv::Alias>(name, ty);
@@ -553,10 +507,12 @@
 
 const spirv::StorageTexture* TypeManager::StorageTexture(
     ast::TextureDimension dims,
-    ast::ImageFormat fmt) {
+    ast::ImageFormat fmt,
+    ast::Access access) {
   return utils::GetOrCreate(
-      state->storage_textures_, spirv::StorageTexture(dims, fmt), [&] {
-        return state->allocator_.Create<spirv::StorageTexture>(dims, fmt);
+      state->storage_textures_, spirv::StorageTexture(dims, fmt, access), [&] {
+        return state->allocator_.Create<spirv::StorageTexture>(dims, fmt,
+                                                               access);
       });
 }
 
@@ -614,12 +570,6 @@
   return ss.str();
 }
 
-std::string AccessControl::String() const {
-  std::stringstream ss;
-  ss << "[[access(" << access << ")]] " << type->String();
-  return ss.str();
-}
-
 std::string Sampler::String() const {
   switch (kind) {
     case ast::SamplerKind::kSampler:
@@ -650,7 +600,7 @@
 
 std::string StorageTexture::String() const {
   std::stringstream ss;
-  ss << "texture_storage_" << dims << "<" << format << ">";
+  ss << "texture_storage_" << dims << "<" << format << ", " << access << ">";
   return ss.str();
 }
 
diff --git a/src/reader/spirv/parser_type.h b/src/reader/spirv/parser_type.h
index c01c4e1..85d80cd 100644
--- a/src/reader/spirv/parser_type.h
+++ b/src/reader/spirv/parser_type.h
@@ -19,7 +19,7 @@
 #include <string>
 #include <vector>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/sampler.h"
 #include "src/ast/storage_class.h"
 #include "src/ast/storage_texture.h"
@@ -56,9 +56,6 @@
   /// @returns the inner most aliased type if this is an alias, `this` otherwise
   const Type* UnwrapAlias() const;
 
-  /// @returns the type with all aliasing and access control removed
-  const Type* UnwrapAliasAndAccess() const;
-
   /// @returns the type with all aliasing, access control and pointers removed
   const Type* UnwrapAll() const;
 
@@ -290,33 +287,6 @@
   uint32_t const stride;
 };
 
-/// `[[access]]` type
-struct AccessControl : public Castable<AccessControl, Type> {
-  /// Constructor
-  /// @param ty the inner type
-  /// @param ac the access control
-  AccessControl(const Type* ty, ast::AccessControl::Access ac);
-
-  /// Copy constructor
-  /// @param other the other type to copy
-  AccessControl(const AccessControl& other);
-
-  /// @return the
-  /// @param b the ProgramBuilder used to construct the AST types
-  /// @returns the constructed ast::Type node for the given type
-  ast::Type* Build(ProgramBuilder& b) const override;
-
-#ifndef NDEBUG
-  /// @returns a string representation of the type, for debug purposes only
-  std::string String() const override;
-#endif  // NDEBUG
-
-  /// the inner type
-  Type const* const type;
-  /// the access control
-  ast::AccessControl::Access const access;
-};
-
 /// `sampler` type
 struct Sampler : public Castable<Sampler, Type> {
   /// Constructor
@@ -427,7 +397,8 @@
   /// Constructor
   /// @param d the texture dimensions
   /// @param f the storage image format
-  StorageTexture(ast::TextureDimension d, ast::ImageFormat f);
+  /// @param a the access control
+  StorageTexture(ast::TextureDimension d, ast::ImageFormat f, ast::Access a);
 
   /// Copy constructor
   /// @param other the other type to copy
@@ -444,6 +415,9 @@
 
   /// the storage image format
   ast::ImageFormat const format;
+
+  /// the access control
+  ast::Access const access;
 };
 
 /// Base class for named types
@@ -556,12 +530,6 @@
   /// @return a Array type. Repeated calls with the same arguments will return
   /// the same pointer.
   const spirv::Array* Array(const Type* el, uint32_t sz, uint32_t st);
-  /// @param ty the inner type
-  /// @param ac the access control
-  /// @return a AccessControl type. Repeated calls with the same arguments will
-  /// return the same pointer.
-  const spirv::AccessControl* AccessControl(const Type* ty,
-                                            ast::AccessControl::Access ac);
   /// @param n the alias name
   /// @param t the aliased type
   /// @return a Alias type. Repeated calls with the same arguments will return
@@ -594,10 +562,12 @@
                                               const Type* t);
   /// @param d the texture dimensions
   /// @param f the storage image format
+  /// @param a the access control
   /// @return a StorageTexture type. Repeated calls with the same arguments will
   /// return the same pointer.
   const spirv::StorageTexture* StorageTexture(ast::TextureDimension d,
-                                              ast::ImageFormat f);
+                                              ast::ImageFormat f,
+                                              ast::Access a);
 
  private:
   struct State;
diff --git a/src/reader/spirv/parser_type_test.cc b/src/reader/spirv/parser_type_test.cc
index fa89aa3..8818e3f 100644
--- a/src/reader/spirv/parser_type_test.cc
+++ b/src/reader/spirv/parser_type_test.cc
@@ -35,8 +35,6 @@
   EXPECT_EQ(ty.Vector(ty.I32(), 3), ty.Vector(ty.I32(), 3));
   EXPECT_EQ(ty.Matrix(ty.I32(), 3, 2), ty.Matrix(ty.I32(), 3, 2));
   EXPECT_EQ(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 3, 2));
-  EXPECT_EQ(ty.AccessControl(ty.I32(), ast::AccessControl::kRead),
-            ty.AccessControl(ty.I32(), ast::AccessControl::kRead));
   EXPECT_EQ(ty.Alias(sym, ty.I32()), ty.Alias(sym, ty.I32()));
   EXPECT_EQ(ty.Struct(sym, {ty.I32()}), ty.Struct(sym, {ty.I32()}));
   EXPECT_EQ(ty.Sampler(ast::SamplerKind::kSampler),
@@ -47,10 +45,10 @@
             ty.MultisampledTexture(ast::TextureDimension::k2d, ty.I32()));
   EXPECT_EQ(ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()),
             ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()));
-  EXPECT_EQ(
-      ty.StorageTexture(ast::TextureDimension::k2d, ast::ImageFormat::kR16Sint),
-      ty.StorageTexture(ast::TextureDimension::k2d,
-                        ast::ImageFormat::kR16Sint));
+  EXPECT_EQ(ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kRead),
+            ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kRead));
 }
 
 TEST(SpvParserTypeTest, DifferentArgumentsGivesDifferentPointer) {
@@ -70,10 +68,6 @@
   EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.U32(), 3, 2));
   EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 2, 2));
   EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 3, 3));
-  EXPECT_NE(ty.AccessControl(ty.I32(), ast::AccessControl::kRead),
-            ty.AccessControl(ty.U32(), ast::AccessControl::kRead));
-  EXPECT_NE(ty.AccessControl(ty.I32(), ast::AccessControl::kRead),
-            ty.AccessControl(ty.I32(), ast::AccessControl::kWrite));
   EXPECT_NE(ty.Alias(sym_a, ty.I32()), ty.Alias(sym_b, ty.I32()));
   EXPECT_NE(ty.Struct(sym_a, {ty.I32()}), ty.Struct(sym_b, {ty.I32()}));
   EXPECT_NE(ty.Sampler(ast::SamplerKind::kSampler),
@@ -88,14 +82,18 @@
             ty.SampledTexture(ast::TextureDimension::k3d, ty.I32()));
   EXPECT_NE(ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()),
             ty.SampledTexture(ast::TextureDimension::k2d, ty.U32()));
-  EXPECT_NE(
-      ty.StorageTexture(ast::TextureDimension::k2d, ast::ImageFormat::kR16Sint),
-      ty.StorageTexture(ast::TextureDimension::k3d,
-                        ast::ImageFormat::kR16Sint));
-  EXPECT_NE(
-      ty.StorageTexture(ast::TextureDimension::k2d, ast::ImageFormat::kR16Sint),
-      ty.StorageTexture(ast::TextureDimension::k2d,
-                        ast::ImageFormat::kR32Sint));
+  EXPECT_NE(ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kRead),
+            ty.StorageTexture(ast::TextureDimension::k3d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kRead));
+  EXPECT_NE(ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kRead),
+            ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR32Sint, ast::Access::kRead));
+  EXPECT_NE(ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kRead),
+            ty.StorageTexture(ast::TextureDimension::k2d,
+                              ast::ImageFormat::kR16Sint, ast::Access::kWrite));
 }
 
 }  // namespace
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 67bd0a3..a45fe37 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -66,9 +66,9 @@
 const char kFragmentStage[] = "fragment";
 const char kComputeStage[] = "compute";
 
-const char kReadAccessControl[] = "read";
-const char kWriteAccessControl[] = "write";
-const char kReadWriteAccessControl[] = "read_write";
+const char kReadAccess[] = "read";
+const char kWriteAccess[] = "write";
+const char kReadWriteAccess[] = "read_write";
 
 ast::Builtin ident_to_builtin(const std::string& str) {
   if (str == "position") {
@@ -204,9 +204,13 @@
 ParserImpl::TypedIdentifier::TypedIdentifier(const TypedIdentifier&) = default;
 
 ParserImpl::TypedIdentifier::TypedIdentifier(ast::Type* type_in,
+                                             ast::Access access_in,
                                              std::string name_in,
                                              Source source_in)
-    : type(type_in), name(std::move(name_in)), source(std::move(source_in)) {}
+    : type(type_in),
+      access(access_in),
+      name(std::move(name_in)),
+      source(std::move(source_in)) {}
 
 ParserImpl::TypedIdentifier::~TypedIdentifier() = default;
 
@@ -237,10 +241,12 @@
 ParserImpl::VarDeclInfo::VarDeclInfo(Source source_in,
                                      std::string name_in,
                                      ast::StorageClass storage_class_in,
+                                     ast::Access access_in,
                                      ast::Type* type_in)
     : source(std::move(source_in)),
       name(std::move(name_in)),
       storage_class(storage_class_in),
+      access(access_in),
       type(type_in) {}
 
 ParserImpl::VarDeclInfo::~VarDeclInfo() = default;
@@ -477,7 +483,8 @@
   return create<ast::Variable>(
       decl->source,                             // source
       builder_.Symbols().Register(decl->name),  // symbol
-      decl->storage_class,                      // storage_class
+      decl->storage_class,                      // storage class
+      decl->access,                             // access control
       decl->type,                               // type
       false,                                    // is_const
       constructor,                              // constructor
@@ -511,7 +518,8 @@
   return create<ast::Variable>(
       decl->source,                             // source
       builder_.Symbols().Register(decl->name),  // symbol
-      ast::StorageClass::kNone,                 // storage_class
+      ast::StorageClass::kNone,                 // storage class
+      ast::Access::kUndefined,                  // access control
       decl->type,                               // type
       true,                                     // is_const
       initializer,                              // constructor
@@ -519,26 +527,27 @@
 }
 
 // variable_decl
-//   : VAR variable_storage_decoration? variable_ident_decl
+//   : VAR variable_qualifier? variable_ident_decl
 Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl(bool allow_inferred) {
-  if (!match(Token::Type::kVar))
+  Source source;
+  if (!match(Token::Type::kVar, &source))
     return Failure::kNoMatch;
 
-  ast::StorageClass sc = ast::StorageClass::kNone;
-  auto explicit_sc = variable_storage_decoration();
-  if (explicit_sc.errored)
+  VariableQualifier vq;
+  auto explicit_vq = variable_qualifier();
+  if (explicit_vq.errored)
     return Failure::kErrored;
-  if (explicit_sc.matched) {
-    sc = explicit_sc.value;
+  if (explicit_vq.matched) {
+    vq = explicit_vq.value;
 
     // TODO(crbug.com/tint/697): Remove this.
-    if (sc == ast::StorageClass::kInput) {
-      deprecated(explicit_sc.source,
+    if (vq.storage_class == ast::StorageClass::kInput) {
+      deprecated(explicit_vq.source,
                  "use an entry point parameter instead of a variable in the "
                  "`in` storage class");
     }
-    if (sc == ast::StorageClass::kOutput) {
-      deprecated(explicit_sc.source,
+    if (vq.storage_class == ast::StorageClass::kOutput) {
+      deprecated(explicit_vq.source,
                  "use an entry point return value instead of a variable in the "
                  "`out` storage class");
     }
@@ -549,7 +558,20 @@
   if (decl.errored)
     return Failure::kErrored;
 
-  return VarDeclInfo{decl->source, decl->name, sc, decl->type};
+  auto access = vq.access;
+
+  if (access == ast::Access::kUndefined &&
+      decl->access != ast::Access::kUndefined) {
+    // TODO(crbug.com/tint/846): Remove this
+    access = decl->access;
+    std::stringstream msg;
+    msg << "declare access with var<" << vq.storage_class << ", " << access
+        << "> instead of using [[access]] decoration";
+    deprecated(source, msg.str());
+  }
+
+  return VarDeclInfo{decl->source, decl->name, vq.storage_class, access,
+                     decl->type};
 }
 
 // texture_sampler_types
@@ -559,7 +581,8 @@
 //  | multisampled_texture_type LESS_THAN type_decl GREATER_THAN
 //  | storage_texture_type LESS_THAN image_storage_type
 //                         COMMA access GREATER_THAN
-Maybe<ast::Type*> ParserImpl::texture_sampler_types() {
+Maybe<ast::Type*> ParserImpl::texture_sampler_types(
+    ast::DecorationList& decos) {
   auto type = sampler_type();
   if (type.matched)
     return type;
@@ -601,7 +624,7 @@
   if (storage.matched) {
     const char* use = "storage texture type";
     using StorageTextureInfo =
-        std::pair<tint::ast::ImageFormat, tint::ast::AccessControl::Access>;
+        std::pair<tint::ast::ImageFormat, tint::ast::Access>;
     auto params = expect_lt_gt_block(use, [&]() -> Expect<StorageTextureInfo> {
       auto format = expect_image_storage_type(use);
       if (format.errored) {
@@ -609,14 +632,23 @@
       }
 
       if (!match(Token::Type::kComma)) {
+        // TODO(crbug.com/tint/846): Remove this, along with the decos parameter
+        auto access_decos = take_decorations<ast::AccessDecoration>(decos);
+        if (access_decos.size() > 1) {
+          return add_error(access_decos[1]->source(),
+                           "multiple access decorations not allowed");
+        }
+        if (access_decos.size() == 0) {
+          return add_error(source_range, "expected access control");
+        }
+
         deprecated(
             peek().source(),
             "access control is expected as last parameter of storage textures");
-        return std::make_pair(format.value,
-                              tint::ast::AccessControl::kReadWrite);
+        return std::make_pair(format.value, access_decos[0]->value());
       }
 
-      auto access = expect_access_type();
+      auto access = expect_access("access control");
       if (access.errored) {
         return Failure::kErrored;
       }
@@ -628,19 +660,8 @@
       return Failure::kErrored;
     }
 
-    ast::Type* ty =
-        builder_.ty.storage_texture(source_range, storage.value, params->first);
-
-    if (params->second != tint::ast::AccessControl::kReadWrite) {
-      // TODO(crbug.com/tint/846): The ast::AccessControl decoration is
-      // deprecated, but while we're migrating existing WGSL over to the new
-      // style of having the access part of the storage texture, we need to
-      // support both old and new styles. For now, have the new syntax emulate
-      // the old style AST.
-      ty = builder_.ty.access(params->second, ty);
-    }
-
-    return ty;
+    return builder_.ty.storage_texture(source_range, storage.value,
+                                       params->first, params->second);
   }
 
   return Failure::kNoMatch;
@@ -906,7 +927,8 @@
     return Failure::kErrored;
 
   if (allow_inferred && !peek().Is(Token::Type::kColon)) {
-    return TypedIdentifier{nullptr, ident.value, ident.source};
+    return TypedIdentifier{nullptr, ast::Access::kUndefined, ident.value,
+                           ident.source};
   }
 
   if (!expect(use, Token::Type::kColon))
@@ -916,8 +938,6 @@
   if (decos.errored)
     return Failure::kErrored;
 
-  auto access_decos = take_decorations<ast::AccessDecoration>(decos.value);
-
   auto t = peek();
   auto type = type_decl(decos.value);
   if (type.errored)
@@ -925,52 +945,65 @@
   if (!type.matched)
     return add_error(t.source(), "invalid type", use);
 
+  auto access_decos = take_decorations<ast::AccessDecoration>(decos.value);
+
   if (!expect_decorations_consumed(decos.value))
     return Failure::kErrored;
 
   if (access_decos.size() > 1)
     return add_error(ident.source, "multiple access decorations not allowed");
 
-  ast::Type* ty = type.value;
+  auto access =
+      access_decos.empty() ? ast::Access::kUndefined : access_decos[0]->value();
 
-  for (auto* deco : access_decos) {
-    // If we have an access control decoration then we take it and wrap our
-    // type up with that decoration
-    ty = builder_.ty.access(deco->source(),
-                            deco->As<ast::AccessDecoration>()->value(), ty);
-  }
-  return TypedIdentifier{ty, ident.value, ident.source};
+  return TypedIdentifier{type.value, access, ident.value, ident.source};
 }
 
-Expect<ast::AccessControl::Access> ParserImpl::expect_access_type() {
-  auto ident = expect_ident("access_type");
+Expect<ast::Access> ParserImpl::expect_access(const std::string& use) {
+  auto ident = expect_ident(use);
   if (ident.errored)
     return Failure::kErrored;
 
-  if (ident.value == kReadAccessControl)
-    return {ast::AccessControl::kRead, ident.source};
-  if (ident.value == kWriteAccessControl)
-    return {ast::AccessControl::kWrite, ident.source};
-  if (ident.value == kReadWriteAccessControl)
-    return {ast::AccessControl::kReadWrite, ident.source};
+  if (ident.value == kReadAccess)
+    return {ast::Access::kRead, ident.source};
+  if (ident.value == kWriteAccess)
+    return {ast::Access::kWrite, ident.source};
+  if (ident.value == kReadWriteAccess)
+    return {ast::Access::kReadWrite, ident.source};
 
   return add_error(ident.source, "invalid value for access decoration");
 }
 
-// variable_storage_decoration
-//   : LESS_THAN storage_class GREATER_THAN
-Maybe<ast::StorageClass> ParserImpl::variable_storage_decoration() {
-  if (!peek().IsLessThan())
+// variable_qualifier
+//   : LESS_THAN storage_class (COMMA access_mode)? GREATER_THAN
+Maybe<ParserImpl::VariableQualifier> ParserImpl::variable_qualifier() {
+  if (!peek().IsLessThan()) {
     return Failure::kNoMatch;
+  }
 
-  const char* use = "variable decoration";
+  auto* use = "variable declaration";
+  auto vq = expect_lt_gt_block(use, [&]() -> Expect<VariableQualifier> {
+    auto source = make_source_range();
+    auto sc = expect_storage_class(use);
+    if (sc.errored) {
+      return Failure::kErrored;
+    }
+    if (match(Token::Type::kComma)) {
+      auto ac = expect_access(use);
+      if (ac.errored) {
+        return Failure::kErrored;
+      }
+      return VariableQualifier{sc.value, ac.value};
+    }
+    return Expect<VariableQualifier>{
+        VariableQualifier{sc.value, ast::Access::kUndefined}, source};
+  });
 
-  auto sc = expect_lt_gt_block(use, [&] { return expect_storage_class(use); });
-
-  if (sc.errored)
+  if (vq.errored) {
     return Failure::kErrored;
+  }
 
-  return sc;
+  return vq;
 }
 
 // type_alias
@@ -1088,7 +1121,7 @@
     return expect_type_decl_matrix(t);
   }
 
-  auto texture_or_sampler = texture_sampler_types();
+  auto texture_or_sampler = texture_sampler_types(decos);
   if (texture_or_sampler.errored)
     return Failure::kErrored;
   if (texture_or_sampler.matched)
@@ -1449,7 +1482,8 @@
   auto* var =
       create<ast::Variable>(decl->source,                             // source
                             builder_.Symbols().Register(decl->name),  // symbol
-                            ast::StorageClass::kNone,  // storage_class
+                            ast::StorageClass::kNone,  // storage class
+                            ast::Access::kUndefined,   // access control
                             decl->type,                // type
                             true,                      // is_const
                             nullptr,                   // constructor
@@ -1712,7 +1746,8 @@
     auto* var = create<ast::Variable>(
         decl->source,                             // source
         builder_.Symbols().Register(decl->name),  // symbol
-        ast::StorageClass::kNone,                 // storage_class
+        ast::StorageClass::kNone,                 // storage class
+        ast::Access::kUndefined,                  // access control
         decl->type,                               // type
         true,                                     // is_const
         constructor.value,                        // constructor
@@ -1741,7 +1776,8 @@
   auto* var =
       create<ast::Variable>(decl->source,                             // source
                             builder_.Symbols().Register(decl->name),  // symbol
-                            decl->storage_class,     // storage_class
+                            decl->storage_class,     // storage class
+                            decl->access,            // access control
                             decl->type,              // type
                             false,                   // is_const
                             constructor,             // constructor
@@ -2947,7 +2983,7 @@
   if (s == kAccessDecoration) {
     const char* use = "access decoration";
     return expect_paren_block(use, [&]() -> Result {
-      auto val = expect_access_type();
+      auto val = expect_access("access control");
       if (val.errored)
         return Failure::kErrored;
 
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 5a58359..afbcce1 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -22,7 +22,7 @@
 #include <utility>
 #include <vector>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/program_builder.h"
 #include "src/reader/wgsl/parser_impl_detail.h"
 #include "src/reader/wgsl/token.h"
@@ -208,14 +208,20 @@
     TypedIdentifier(const TypedIdentifier& other);
     /// Constructor
     /// @param type_in parsed type
+    /// @param access_in parsed access
     /// @param name_in parsed identifier
     /// @param source_in source to the identifier
-    TypedIdentifier(ast::Type* type_in, std::string name_in, Source source_in);
+    TypedIdentifier(ast::Type* type_in,
+                    ast::Access access_in,
+                    std::string name_in,
+                    Source source_in);
     /// Destructor
     ~TypedIdentifier();
 
     /// Parsed type. May be nullptr for inferred types.
     ast::Type* type = nullptr;
+    /// The access control. TODO(crbug.com/tint/846): Remove
+    ast::Access access = ast::Access::kUndefined;
     /// Parsed identifier.
     std::string name;
     /// Source to the identifier.
@@ -270,10 +276,12 @@
     /// @param source_in variable declaration source
     /// @param name_in variable name
     /// @param storage_class_in variable storage class
+    /// @param access_in variable access control
     /// @param type_in variable type
     VarDeclInfo(Source source_in,
                 std::string name_in,
                 ast::StorageClass storage_class_in,
+                ast::Access access_in,
                 ast::Type* type_in);
     /// Destructor
     ~VarDeclInfo();
@@ -283,11 +291,21 @@
     /// Variable name
     std::string name;
     /// Variable storage class
-    ast::StorageClass storage_class;
+    ast::StorageClass storage_class = ast::StorageClass::kNone;
+    /// Variable access control
+    ast::Access access = ast::Access::kUndefined;
     /// Variable type
     ast::Type* type = nullptr;
   };
 
+  /// VariableQualifier contains the parsed information for a variable qualifier
+  struct VariableQualifier {
+    /// The variable's storage class
+    ast::StorageClass storage_class = ast::StorageClass::kNone;
+    /// The variable's access control
+    ast::Access access = ast::Access::kUndefined;
+  };
+
   /// Creates a new parser using the given file
   /// @param file the input source file to parse
   explicit ParserImpl(Source::File const* file);
@@ -400,9 +418,9 @@
   Expect<TypedIdentifier> expect_variable_ident_decl(
       const std::string& use,
       bool allow_inferred = false);
-  /// Parses a `variable_storage_decoration` grammar element
-  /// @returns the storage class or StorageClass::kNone if none matched
-  Maybe<ast::StorageClass> variable_storage_decoration();
+  /// Parses a `variable_qualifier` grammar element
+  /// @returns the variable qualifier information
+  Maybe<VariableQualifier> variable_qualifier();
   /// Parses a `type_alias` grammar element
   /// @returns the type alias or nullptr on error
   Maybe<ast::Alias*> type_alias();
@@ -438,8 +456,10 @@
   /// @returns the parsed function, nullptr otherwise
   Maybe<ast::Function*> function_decl(ast::DecorationList& decos);
   /// Parses a `texture_sampler_types` grammar element
+  /// TODO(crbug.com/tint/864): Remove decos parameter
+  /// @param decos the list of decorations for the type declaration.
   /// @returns the parsed Type or nullptr if none matched.
-  Maybe<ast::Type*> texture_sampler_types();
+  Maybe<ast::Type*> texture_sampler_types(ast::DecorationList& decos);
   /// Parses a `sampler_type` grammar element
   /// @returns the parsed Type or nullptr if none matched.
   Maybe<ast::Type*> sampler_type();
@@ -477,10 +497,11 @@
   /// not match a stage name.
   /// @returns the pipeline stage.
   Expect<ast::PipelineStage> expect_pipeline_stage();
-  /// Parses an access type identifier, erroring if the next token does not
-  /// match a valid access type name.
+  /// Parses an access control identifier, erroring if the next token does not
+  /// match a valid access control.
+  /// @param use a description of what was being parsed if an error was raised
   /// @returns the parsed access control.
-  Expect<ast::AccessControl::Access> expect_access_type();
+  Expect<ast::Access> expect_access(const std::string& use);
   /// Parses a builtin identifier, erroring if the next token does not match a
   /// valid builtin name.
   /// @returns the parsed builtin.
diff --git a/src/reader/wgsl/parser_impl_error_msg_test.cc b/src/reader/wgsl/parser_impl_error_msg_test.cc
index dfb4c7f..0de7480 100644
--- a/src/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/reader/wgsl/parser_impl_error_msg_test.cc
@@ -548,31 +548,31 @@
 }
 
 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");
+  EXPECT("var x : texture_storage_2d;",
+         "test.wgsl:1:27 error: expected '<' for storage texture type\n"
+         "var x : texture_storage_2d;\n"
+         "                          ^\n");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingGreaterThan) {
-  EXPECT("var x : [[access(read)]] texture_storage_2d<r8uint, read;",
-         "test.wgsl:1:57 error: expected '>' for storage texture type\n"
-         "var x : [[access(read)]] texture_storage_2d<r8uint, read;\n"
-         "                                                        ^\n");
+  EXPECT("var x : texture_storage_2d<r8uint, read;",
+         "test.wgsl:1:40 error: expected '>' for storage texture type\n"
+         "var x : texture_storage_2d<r8uint, read;\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");
+  EXPECT("var x : texture_storage_2d<>;",
+         "test.wgsl:1:28 error: invalid format for storage texture type\n"
+         "var x : 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");
+  EXPECT("var x : texture_storage_2d<1>;",
+         "test.wgsl:1:28 error: invalid format for storage texture type\n"
+         "var x : texture_storage_2d<1>;\n"
+         "                           ^\n");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclStructDecoMissingStruct) {
@@ -1018,14 +1018,14 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarStorageDeclInvalidClass) {
   EXPECT("var<fish> i : i32",
-         "test.wgsl:1:5 error: invalid storage class for variable decoration\n"
+         "test.wgsl:1:5 error: invalid storage class for variable declaration\n"
          "var<fish> i : i32\n"
          "    ^^^^\n");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarStorageDeclMissingGThan) {
   EXPECT("var<in i : i32",
-         "test.wgsl:1:8 error: expected '>' for variable decoration\n"
+         "test.wgsl:1:8 error: expected '>' for variable declaration\n"
          "var<in i : i32\n"
          "       ^\n");
 }
diff --git a/src/reader/wgsl/parser_impl_global_variable_decl_test.cc b/src/reader/wgsl/parser_impl_global_variable_decl_test.cc
index 7887b02..a57fbb6 100644
--- a/src/reader/wgsl/parser_impl_global_variable_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_global_variable_decl_test.cc
@@ -165,7 +165,7 @@
   EXPECT_TRUE(e.errored);
   EXPECT_FALSE(e.matched);
   EXPECT_EQ(e.value, nullptr);
-  EXPECT_EQ(p->error(), "1:5: invalid storage class for variable decoration");
+  EXPECT_EQ(p->error(), "1:5: invalid storage class for variable declaration");
 }
 
 TEST_F(ParserImplTest, GlobalVariableDecl_StorageClassIn_Deprecated) {
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 a574a06..76a0528 100644
--- a/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
+++ b/src/reader/wgsl/parser_impl_texture_sampler_types_test.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/ast/access_decoration.h"
 #include "src/reader/wgsl/parser_impl_test_helper.h"
 #include "src/sem/depth_texture_type.h"
 #include "src/sem/multisampled_texture_type.h"
@@ -24,7 +25,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_Invalid) {
   auto p = parser("1234");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -33,7 +35,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_Sampler) {
   auto p = parser("sampler");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -45,7 +48,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SamplerComparison) {
   auto p = parser("sampler_comparison");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -57,7 +61,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_DepthTexture) {
   auto p = parser("texture_depth_2d");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -70,7 +75,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_F32) {
   auto p = parser("texture_1d<f32>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -84,7 +90,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_I32) {
   auto p = parser("texture_2d<i32>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -98,7 +105,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_U32) {
   auto p = parser("texture_3d<u32>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -112,7 +120,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_Invalid) {
   auto p = parser("texture_1d<abc>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
@@ -122,7 +131,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingType) {
   auto p = parser("texture_1d<>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
@@ -132,7 +142,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingLessThan) {
   auto p = parser("texture_1d");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
@@ -142,7 +153,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingGreaterThan) {
   auto p = parser("texture_1d<u32");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
@@ -152,7 +164,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_I32) {
   auto p = parser("texture_multisampled_2d<i32>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -166,7 +179,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_Invalid) {
   auto p = parser("texture_multisampled_2d<abc>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
@@ -176,7 +190,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_MissingType) {
   auto p = parser("texture_multisampled_2d<>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_TRUE(p->has_error());
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
@@ -187,7 +202,8 @@
 TEST_F(ParserImplTest,
        TextureSamplerTypes_MultisampledTexture_MissingLessThan) {
   auto p = parser("texture_multisampled_2d");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
@@ -197,7 +213,8 @@
 TEST_F(ParserImplTest,
        TextureSamplerTypes_MultisampledTexture_MissingGreaterThan) {
   auto p = parser("texture_multisampled_2d<u32");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
@@ -208,7 +225,8 @@
 TEST_F(ParserImplTest,
        TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm_DEPRECATED) {
   auto p = parser("texture_storage_1d<r8unorm>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos{create<ast::AccessDecoration>(ast::Access::kRead)};
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -218,26 +236,25 @@
   ASSERT_TRUE(t->Is<ast::StorageTexture>());
   EXPECT_EQ(t->As<ast::StorageTexture>()->image_format(),
             ast::ImageFormat::kR8Unorm);
+  EXPECT_EQ(t->As<ast::StorageTexture>()->access(), ast::Access::kRead);
   EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k1d);
   EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 28u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Readonly1dR8Unorm) {
   auto p = parser("texture_storage_1d<r8unorm, read>");
-  auto a = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_TRUE(a.matched);
-  EXPECT_FALSE(a.errored);
-  ASSERT_NE(a.value, nullptr);
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  ASSERT_NE(t.value, nullptr);
 
-  ASSERT_TRUE(a->Is<ast::AccessControl>());
-  EXPECT_TRUE(a->As<ast::AccessControl>()->IsReadOnly());
-
-  auto* t = a->As<ast::AccessControl>()->type();
   ASSERT_TRUE(t->Is<ast::Texture>());
   ASSERT_TRUE(t->Is<ast::StorageTexture>());
   EXPECT_EQ(t->As<ast::StorageTexture>()->image_format(),
             ast::ImageFormat::kR8Unorm);
+  EXPECT_EQ(t->As<ast::StorageTexture>()->access(), ast::Access::kRead);
   EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k1d);
   EXPECT_EQ(t->source().range, (Source::Range{{1u, 1u}, {1u, 34u}}));
 }
@@ -246,7 +263,8 @@
 TEST_F(ParserImplTest,
        TextureSamplerTypes_StorageTexture_Writeonly2dR16Float_DEPRECATED) {
   auto p = parser("texture_storage_2d<r16float>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos{create<ast::AccessDecoration>(ast::Access::kWrite)};
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
   EXPECT_TRUE(t.matched);
   EXPECT_FALSE(t.errored);
@@ -256,33 +274,33 @@
   ASSERT_TRUE(t->Is<ast::StorageTexture>());
   EXPECT_EQ(t->As<ast::StorageTexture>()->image_format(),
             ast::ImageFormat::kR16Float);
+  EXPECT_EQ(t->As<ast::StorageTexture>()->access(), ast::Access::kWrite);
   EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
   EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 29u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Writeonly2dR16Float) {
   auto p = parser("texture_storage_2d<r16float, write>");
-  auto a = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_TRUE(a.matched);
-  EXPECT_FALSE(a.errored);
-  ASSERT_NE(a.value, nullptr);
+  EXPECT_TRUE(t.matched);
+  EXPECT_FALSE(t.errored);
+  ASSERT_NE(t.value, nullptr);
 
-  ASSERT_TRUE(a->Is<ast::AccessControl>());
-  EXPECT_TRUE(a->As<ast::AccessControl>()->IsWriteOnly());
-
-  auto* t = a->As<ast::AccessControl>()->type();
   ASSERT_TRUE(t->Is<ast::Texture>());
   ASSERT_TRUE(t->Is<ast::StorageTexture>());
   EXPECT_EQ(t->As<ast::StorageTexture>()->image_format(),
             ast::ImageFormat::kR16Float);
+  EXPECT_EQ(t->As<ast::StorageTexture>()->access(), ast::Access::kWrite);
   EXPECT_EQ(t->As<ast::Texture>()->dim(), ast::TextureDimension::k2d);
   EXPECT_EQ(t->source().range, (Source::Range{{1u, 1u}, {1u, 36u}}));
 }
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidType) {
   auto p = parser("texture_storage_1d<abc, read>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
@@ -291,7 +309,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidAccess) {
   auto p = parser("texture_storage_1d<r16float, abc>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
@@ -300,7 +319,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingType) {
   auto p = parser("texture_storage_1d<>");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
@@ -309,7 +329,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingLessThan) {
   auto p = parser("texture_storage_1d");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
@@ -318,7 +339,8 @@
 
 TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingGreaterThan) {
   auto p = parser("texture_storage_1d<r8unorm, read");
-  auto t = p->texture_sampler_types();
+  ast::DecorationList decos;
+  auto t = p->texture_sampler_types(decos);
   EXPECT_EQ(t.value, nullptr);
   EXPECT_FALSE(t.matched);
   EXPECT_TRUE(t.errored);
diff --git a/src/reader/wgsl/parser_impl_variable_decl_test.cc b/src/reader/wgsl/parser_impl_variable_decl_test.cc
index 367b3d4..efb91d8 100644
--- a/src/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -87,7 +87,7 @@
   EXPECT_FALSE(v.matched);
   EXPECT_TRUE(v.errored);
   EXPECT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:5: invalid storage class for variable decoration");
+  EXPECT_EQ(p->error(), "1:5: invalid storage class for variable declaration");
 }
 
 }  // namespace
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 35cd419..59b8b83 100644
--- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -84,7 +84,9 @@
   ASSERT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithTextureAccessDeco_Read) {
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest,
+       VariableIdentDecl_ParsesWithTextureAccessDeco_Read_DEPRECATED) {
   auto p = parser("my_var : [[access(read)]] texture_storage_1d<r32float>");
 
   auto decl = p->expect_variable_ident_decl("test");
@@ -92,13 +94,17 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<ast::AccessControl>());
-  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsReadOnly());
-  ASSERT_TRUE(
-      decl->type->As<ast::AccessControl>()->type()->Is<ast::StorageTexture>());
+  ASSERT_TRUE(decl->type->Is<ast::StorageTexture>());
+  EXPECT_TRUE(decl->type->As<ast::StorageTexture>()->is_read_only());
+
+  EXPECT_EQ(p->error(),
+            "1:54: use of deprecated language feature: access control is "
+            "expected as last parameter of storage textures");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithTextureAccessDeco_Write) {
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest,
+       VariableIdentDecl_ParsesWithTextureAccessDeco_Write_DEPRECATED) {
   auto p = parser("my_var : [[access(write)]] texture_storage_1d<r32float>");
 
   auto decl = p->expect_variable_ident_decl("test");
@@ -106,14 +112,17 @@
   ASSERT_FALSE(decl.errored);
   ASSERT_EQ(decl->name, "my_var");
   ASSERT_NE(decl->type, nullptr);
-  ASSERT_TRUE(decl->type->Is<ast::AccessControl>());
-  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsWriteOnly());
-  ASSERT_TRUE(
-      decl->type->As<ast::AccessControl>()->type()->Is<ast::StorageTexture>());
+  ASSERT_TRUE(decl->type->Is<ast::StorageTexture>());
+  EXPECT_TRUE(decl->type->As<ast::StorageTexture>()->is_write_only());
+
+  EXPECT_EQ(p->error(),
+            "1:55: use of deprecated language feature: access control is "
+            "expected as last parameter of storage textures");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_Read) {
-  auto p = parser("my_var : [[access(read)]] S");
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_Read_DEPRECATED) {
+  auto p = parser("var my_var : [[access(read)]] S;");
 
   auto* mem = Member("a", ty.i32(), ast::DecorationList{});
   ast::StructMemberList members;
@@ -127,17 +136,25 @@
 
   p->register_constructed("S", s);
 
-  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::AccessControl>());
-  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsReadOnly());
+  auto res = p->expect_global_decl();
+  ASSERT_FALSE(res.errored) << p->error();
+  ASSERT_NE(p->builder().AST().GlobalVariables().size(), 0u);
+  auto* decl = p->builder().AST().GlobalVariables()[0];
+  ASSERT_NE(decl, nullptr);
+  ASSERT_EQ(decl->symbol(), p->builder().Symbols().Get("my_var"));
+  ASSERT_NE(decl->type(), nullptr);
+  EXPECT_TRUE(decl->type()->Is<ast::TypeName>());
+  EXPECT_EQ(decl->declared_access(), ast::Access::kRead);
+
+  EXPECT_EQ(p->error(),
+            "1:1: use of deprecated language feature: declare access with "
+            "var<none, read> instead of using [[access]] decoration");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_ReadWrite) {
-  auto p = parser("my_var : [[access(read_write)]] S");
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest,
+       VariableIdentDecl_ParsesWithAccessDeco_ReadWrite_DEPRECATED) {
+  auto p = parser("var my_var : [[access(read_write)]] S;");
 
   auto* mem = Member("a", ty.i32(), ast::DecorationList{});
   ast::StructMemberList members;
@@ -151,16 +168,23 @@
 
   p->register_constructed("S", s);
 
-  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::AccessControl>());
-  EXPECT_TRUE(decl->type->As<ast::AccessControl>()->IsReadWrite());
+  auto res = p->expect_global_decl();
+  ASSERT_FALSE(res.errored) << p->error();
+  ASSERT_NE(p->builder().AST().GlobalVariables().size(), 0u);
+  auto* decl = p->builder().AST().GlobalVariables()[0];
+  ASSERT_NE(decl, nullptr);
+  ASSERT_EQ(decl->symbol(), p->builder().Symbols().Get("my_var"));
+  ASSERT_NE(decl->type(), nullptr);
+  EXPECT_TRUE(decl->type()->Is<ast::TypeName>());
+  EXPECT_EQ(decl->declared_access(), ast::Access::kReadWrite);
+
+  EXPECT_EQ(p->error(),
+            "1:1: use of deprecated language feature: declare access with "
+            "var<none, read_write> instead of using [[access]] decoration");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_MultipleAccessDecoFail) {
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest, VariableIdentDecl_MultipleAccessDecoFail_DEPRECATED) {
   auto p = parser("my_var : [[access(read), access(read_write)]] S");
 
   auto* mem = Member("a", ty.i32(), ast::DecorationList{});
@@ -181,7 +205,9 @@
   ASSERT_EQ(p->error(), "1:1: multiple access decorations not allowed");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_MultipleAccessDeco_MultiBlock_Fail) {
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest,
+       VariableIdentDecl_MultipleAccessDeco_MultiBlock_Fail_DEPRECATED) {
   auto p = parser("my_var : [[access(read)]][[access(read_write)]] S");
 
   auto* mem = Member("a", ty.i32(), ast::DecorationList{});
@@ -202,7 +228,8 @@
   ASSERT_EQ(p->error(), "1:1: multiple access decorations not allowed");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_AccessDecoBadValue) {
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest, VariableIdentDecl_AccessDecoBadValue_DEPRECATED) {
   auto p = parser("my_var : [[access(unknown)]] S");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
@@ -210,12 +237,13 @@
   ASSERT_EQ(p->error(), "1:19: invalid value for access decoration");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_AccessDecoIllegalValue) {
+// TODO(crbug.com/tint/846): Remove
+TEST_F(ParserImplTest, VariableIdentDecl_AccessDecoIllegalValue_DEPRECATED) {
   auto p = parser("my_var : [[access(1)]] S");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
   ASSERT_TRUE(decl.errored);
-  ASSERT_EQ(p->error(), "1:19: expected identifier for access_type");
+  ASSERT_EQ(p->error(), "1:19: expected identifier for access control");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_NonAccessDecoFail) {
@@ -240,27 +268,27 @@
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_DecorationMissingRightBlock) {
-  auto p = parser("my_var : [[access(read) S");
+  auto p = parser("my_var : [[stride(4) S");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
   ASSERT_TRUE(decl.errored);
-  ASSERT_EQ(p->error(), "1:25: expected ']]' for decoration list");
+  ASSERT_EQ(p->error(), "1:22: expected ']]' for decoration list");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_DecorationMissingRightParen) {
-  auto p = parser("my_var : [[access(read]] S");
+  auto p = parser("my_var : [[stride(4]] S");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
   ASSERT_TRUE(decl.errored);
-  ASSERT_EQ(p->error(), "1:23: expected ')' for access decoration");
+  ASSERT_EQ(p->error(), "1:20: expected ')' for stride decoration");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_DecorationMissingLeftParen) {
-  auto p = parser("my_var : [[access read)]] S");
+  auto p = parser("my_var : [[stride 4)]] S");
   auto decl = p->expect_variable_ident_decl("test");
   ASSERT_TRUE(p->has_error());
   ASSERT_TRUE(decl.errored);
-  ASSERT_EQ(p->error(), "1:19: expected '(' for access decoration");
+  ASSERT_EQ(p->error(), "1:19: expected '(' for stride decoration");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_DecorationEmpty) {
diff --git a/src/reader/wgsl/parser_impl_variable_qualifier_test.cc b/src/reader/wgsl/parser_impl_variable_qualifier_test.cc
new file mode 100644
index 0000000..b070d24
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_qualifier_test.cc
@@ -0,0 +1,130 @@
+// 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 "src/reader/wgsl/parser_impl_test_helper.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+namespace {
+
+struct VariableStorageData {
+  const char* input;
+  ast::StorageClass storage_class;
+  ast::Access access;
+};
+inline std::ostream& operator<<(std::ostream& out, VariableStorageData data) {
+  out << std::string(data.input);
+  return out;
+}
+
+class VariableQualifierTest
+    : public ParserImplTestWithParam<VariableStorageData> {};
+
+TEST_P(VariableQualifierTest, ParsesStorageClass) {
+  auto params = GetParam();
+  auto p = parser(std::string("<") + params.input + ">");
+
+  auto sc = p->variable_qualifier();
+  EXPECT_FALSE(p->has_error());
+  EXPECT_FALSE(sc.errored);
+  EXPECT_TRUE(sc.matched);
+  EXPECT_EQ(sc->storage_class, params.storage_class);
+  EXPECT_EQ(sc->access, params.access);
+
+  auto t = p->next();
+  EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(
+    ParserImplTest,
+    VariableQualifierTest,
+    testing::Values(
+        VariableStorageData{"in", ast::StorageClass::kInput,
+                            ast::Access::kUndefined},
+        VariableStorageData{"out", ast::StorageClass::kOutput,
+                            ast::Access::kUndefined},
+        VariableStorageData{"uniform", ast::StorageClass::kUniform,
+                            ast::Access::kUndefined},
+        VariableStorageData{"workgroup", ast::StorageClass::kWorkgroup,
+                            ast::Access::kUndefined},
+        VariableStorageData{"storage", ast::StorageClass::kStorage,
+                            ast::Access::kUndefined},
+        VariableStorageData{"storage_buffer", ast::StorageClass::kStorage,
+                            ast::Access::kUndefined},
+        VariableStorageData{"image", ast::StorageClass::kImage,
+                            ast::Access::kUndefined},
+        VariableStorageData{"private", ast::StorageClass::kPrivate,
+                            ast::Access::kUndefined},
+        VariableStorageData{"function", ast::StorageClass::kFunction,
+                            ast::Access::kUndefined},
+        VariableStorageData{"storage, read", ast::StorageClass::kStorage,
+                            ast::Access::kRead},
+        VariableStorageData{"storage, write", ast::StorageClass::kStorage,
+                            ast::Access::kWrite},
+        VariableStorageData{"storage, read_write", ast::StorageClass::kStorage,
+                            ast::Access::kReadWrite}));
+
+TEST_F(ParserImplTest, VariableQualifier_NoMatch) {
+  auto p = parser("<not-a-storage-class>");
+  auto sc = p->variable_qualifier();
+  EXPECT_TRUE(p->has_error());
+  EXPECT_TRUE(sc.errored);
+  EXPECT_FALSE(sc.matched);
+  EXPECT_EQ(p->error(), "1:2: invalid storage class for variable declaration");
+}
+
+TEST_F(ParserImplTest, VariableQualifier_Empty) {
+  auto p = parser("<>");
+  auto sc = p->variable_qualifier();
+  EXPECT_TRUE(p->has_error());
+  EXPECT_TRUE(sc.errored);
+  EXPECT_FALSE(sc.matched);
+  EXPECT_EQ(p->error(), "1:2: invalid storage class for variable declaration");
+}
+
+TEST_F(ParserImplTest, VariableQualifier_MissingLessThan) {
+  auto p = parser("in>");
+  auto sc = p->variable_qualifier();
+  EXPECT_FALSE(p->has_error());
+  EXPECT_FALSE(sc.errored);
+  EXPECT_FALSE(sc.matched);
+
+  auto t = p->next();
+  ASSERT_TRUE(t.IsIn());
+}
+
+TEST_F(ParserImplTest, VariableQualifier_MissingLessThan_AfterSC) {
+  auto p = parser("in, >");
+  auto sc = p->variable_qualifier();
+  EXPECT_FALSE(p->has_error());
+  EXPECT_FALSE(sc.errored);
+  EXPECT_FALSE(sc.matched);
+
+  auto t = p->next();
+  ASSERT_TRUE(t.IsIn());
+}
+
+TEST_F(ParserImplTest, VariableQualifier_MissingGreaterThan) {
+  auto p = parser("<in");
+  auto sc = p->variable_qualifier();
+  EXPECT_TRUE(p->has_error());
+  EXPECT_TRUE(sc.errored);
+  EXPECT_FALSE(sc.matched);
+  EXPECT_EQ(p->error(), "1:4: expected '>' for variable declaration");
+}
+
+}  // namespace
+}  // namespace wgsl
+}  // namespace reader
+}  // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_storage_decoration_test.cc b/src/reader/wgsl/parser_impl_variable_storage_decoration_test.cc
deleted file mode 100644
index 889f631..0000000
--- a/src/reader/wgsl/parser_impl_variable_storage_decoration_test.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// 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 "src/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint {
-namespace reader {
-namespace wgsl {
-namespace {
-
-struct VariableStorageData {
-  const char* input;
-  ast::StorageClass result;
-};
-inline std::ostream& operator<<(std::ostream& out, VariableStorageData data) {
-  out << std::string(data.input);
-  return out;
-}
-
-class VariableStorageTest
-    : public ParserImplTestWithParam<VariableStorageData> {};
-
-TEST_P(VariableStorageTest, Parses) {
-  auto params = GetParam();
-  auto p = parser(std::string("<") + params.input + ">");
-
-  auto sc = p->variable_storage_decoration();
-  EXPECT_FALSE(p->has_error());
-  EXPECT_FALSE(sc.errored);
-  EXPECT_TRUE(sc.matched);
-  EXPECT_EQ(sc.value, params.result);
-
-  auto t = p->next();
-  EXPECT_TRUE(t.IsEof());
-}
-INSTANTIATE_TEST_SUITE_P(
-    ParserImplTest,
-    VariableStorageTest,
-    testing::Values(
-        VariableStorageData{"in", ast::StorageClass::kInput},
-        VariableStorageData{"out", ast::StorageClass::kOutput},
-        VariableStorageData{"uniform", ast::StorageClass::kUniform},
-        VariableStorageData{"workgroup", ast::StorageClass::kWorkgroup},
-        VariableStorageData{"storage", ast::StorageClass::kStorage},
-        VariableStorageData{"storage_buffer", ast::StorageClass::kStorage},
-        VariableStorageData{"image", ast::StorageClass::kImage},
-        VariableStorageData{"private", ast::StorageClass::kPrivate},
-        VariableStorageData{"function", ast::StorageClass::kFunction}));
-
-TEST_F(ParserImplTest, VariableStorageDecoration_NoMatch) {
-  auto p = parser("<not-a-storage-class>");
-  auto sc = p->variable_storage_decoration();
-  EXPECT_TRUE(p->has_error());
-  EXPECT_TRUE(sc.errored);
-  EXPECT_FALSE(sc.matched);
-  EXPECT_EQ(p->error(), "1:2: invalid storage class for variable decoration");
-}
-
-TEST_F(ParserImplTest, VariableStorageDecoration_Empty) {
-  auto p = parser("<>");
-  auto sc = p->variable_storage_decoration();
-  EXPECT_TRUE(p->has_error());
-  EXPECT_TRUE(sc.errored);
-  EXPECT_FALSE(sc.matched);
-  EXPECT_EQ(p->error(), "1:2: invalid storage class for variable decoration");
-}
-
-TEST_F(ParserImplTest, VariableStorageDecoration_MissingLessThan) {
-  auto p = parser("in>");
-  auto sc = p->variable_storage_decoration();
-  EXPECT_FALSE(p->has_error());
-  EXPECT_FALSE(sc.errored);
-  EXPECT_FALSE(sc.matched);
-
-  auto t = p->next();
-  ASSERT_TRUE(t.IsIn());
-}
-
-TEST_F(ParserImplTest, VariableStorageDecoration_MissingGreaterThan) {
-  auto p = parser("<in");
-  auto sc = p->variable_storage_decoration();
-  EXPECT_TRUE(p->has_error());
-  EXPECT_TRUE(sc.errored);
-  EXPECT_FALSE(sc.matched);
-  EXPECT_EQ(p->error(), "1:4: expected '>' for variable decoration");
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace reader
-}  // namespace tint
diff --git a/src/resolver/assignment_validation_test.cc b/src/resolver/assignment_validation_test.cc
index dab2610..b908662 100644
--- a/src/resolver/assignment_validation_test.cc
+++ b/src/resolver/assignment_validation_test.cc
@@ -156,14 +156,14 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignNonStorable_Fail) {
-  // var a : [[access(read)]] texture_storage_1d<rgba8unorm>;
-  // var b : [[access(read)]] texture_storage_1d<rgba8unorm>;
+  // var a : texture_storage_1d<rgba8unorm, read>;
+  // var b : texture_storage_1d<rgba8unorm, read>;
   // a = b;
 
   auto make_type = [&] {
-    auto* tex_type = ty.storage_texture(ast::TextureDimension::k1d,
-                                        ast::ImageFormat::kRgba8Unorm);
-    return ty.access(ast::AccessControl::kRead, tex_type);
+    return ty.storage_texture(ast::TextureDimension::k1d,
+                              ast::ImageFormat::kRgba8Unorm,
+                              ast::Access::kRead);
   };
 
   Global("a", make_type(), ast::StorageClass::kNone,
@@ -182,7 +182,7 @@
   EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(
       r()->error(),
-      R"(12:34 error: '[[access(read)]] texture_storage_1d<rgba8unorm>' is not storable)");
+      R"(12:34 error: 'texture_storage_1d<rgba8unorm, read>' is not storable)");
 }
 
 }  // namespace
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index e24dc70..1de719b 100644
--- a/src/resolver/decoration_validation_test.cc
+++ b/src/resolver/decoration_validation_test.cc
@@ -30,7 +30,6 @@
 namespace {
 
 enum class DecorationKind {
-  kAccess,
   kAlign,
   kBinding,
   kBuiltin,
@@ -68,9 +67,6 @@
                                              ProgramBuilder& builder,
                                              DecorationKind kind) {
   switch (kind) {
-    case DecorationKind::kAccess:
-      return {builder.create<ast::AccessDecoration>(source,
-                                                    ast::AccessControl::kRead)};
     case DecorationKind::kAlign:
       return {builder.create<ast::StructMemberAlignDecoration>(source, 4u)};
     case DecorationKind::kBinding:
@@ -122,8 +118,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     FunctionReturnTypeDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, false},
+    testing::Values(TestParams{DecorationKind::kAlign, false},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, true},
                     TestParams{DecorationKind::kGroup, false},
@@ -162,8 +157,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     ArrayDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, false},
+    testing::Values(TestParams{DecorationKind::kAlign, false},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, false},
                     TestParams{DecorationKind::kGroup, false},
@@ -197,8 +191,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     StructDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, false},
+    testing::Values(TestParams{DecorationKind::kAlign, false},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, false},
                     TestParams{DecorationKind::kGroup, false},
@@ -234,8 +227,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     StructMemberDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, true},
+    testing::Values(TestParams{DecorationKind::kAlign, true},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, true},
                     TestParams{DecorationKind::kGroup, false},
@@ -277,8 +269,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     VariableDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, false},
+    testing::Values(TestParams{DecorationKind::kAlign, false},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, true},
                     TestParams{DecorationKind::kGroup, false},
@@ -312,8 +303,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     ConstantDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, false},
+    testing::Values(TestParams{DecorationKind::kAlign, false},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, false},
                     TestParams{DecorationKind::kGroup, false},
@@ -346,8 +336,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ResolverDecorationValidationTest,
     FunctionDecorationTest,
-    testing::Values(TestParams{DecorationKind::kAccess, false},
-                    TestParams{DecorationKind::kAlign, false},
+    testing::Values(TestParams{DecorationKind::kAlign, false},
                     TestParams{DecorationKind::kBinding, false},
                     TestParams{DecorationKind::kBuiltin, false},
                     TestParams{DecorationKind::kGroup, false},
@@ -523,8 +512,8 @@
 TEST_F(ResourceDecorationTest, StorageBufferMissingBinding) {
   auto* s = Structure("S", {Member("x", ty.i32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{12, 34}}, "G", ac, ast::StorageClass::kStorage);
+  Global(Source{{12, 34}}, "G", s, ast::StorageClass::kStorage,
+         ast::Access::kRead);
 
   EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(r()->error(),
diff --git a/src/resolver/host_shareable_validation_test.cc b/src/resolver/host_shareable_validation_test.cc
index bb73ad9..57e6fbc 100644
--- a/src/resolver/host_shareable_validation_test.cc
+++ b/src/resolver/host_shareable_validation_test.cc
@@ -28,8 +28,9 @@
 TEST_F(ResolverHostShareableValidationTest, BoolMember) {
   auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
+
+  Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -47,8 +48,9 @@
 TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
   auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
+
+  Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -68,10 +70,10 @@
   AST().AddConstructedType(a1);
   auto* s = Structure("S", {Member(Source{{12, 34}}, "x", a1)},
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-  auto* a2 = ty.alias("a2", ac);
+  auto* a2 = ty.alias("a2", s);
   AST().AddConstructedType(a2);
   Global(Source{{56, 78}}, "g", a2, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -93,8 +95,9 @@
 
   auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage,
+
+  Global(Source{{9, 10}}, "g", s, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -135,8 +138,9 @@
 
   auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage,
+
+  Global(Source{{9, 10}}, "g", s, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/resolver/intrinsic_test.cc b/src/resolver/intrinsic_test.cc
index 701796f..39d1b18 100644
--- a/src/resolver/intrinsic_test.cc
+++ b/src/resolver/intrinsic_test.cc
@@ -276,12 +276,11 @@
   auto format = GetParam().format;
 
   auto* coords_type = GetCoordsType(dim, ty.i32());
-  auto* texture_type = ty.storage_texture(dim, format);
-  auto* ro_texture_type = ty.access(ast::AccessControl::kRead, texture_type);
+  auto* texture_type = ty.storage_texture(dim, format, ast::Access::kRead);
 
   ast::ExpressionList call_params;
 
-  add_call_param("texture", ro_texture_type, &call_params);
+  add_call_param("texture", texture_type, &call_params);
   add_call_param("coords", coords_type, &call_params);
 
   if (ast::IsTextureArray(dim)) {
@@ -769,8 +768,7 @@
   auto* ary = ty.array<i32>();
   auto* str = Structure("S", {Member("x", ary)},
                         {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, str);
-  Global("a", ac, ast::StorageClass::kStorage,
+  Global("a", str, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 9f81e32..14f4b7d 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -287,13 +287,6 @@
     if (ty->Is<ast::F32>()) {
       return builder_->create<sem::F32>();
     }
-    if (auto* t = ty->As<ast::AccessControl>()) {
-      TINT_SCOPED_ASSIGNMENT(current_access_control_, t);
-      if (auto* el = Type(t->type())) {
-        return el;
-      }
-      return nullptr;
-    }
     if (auto* t = ty->As<ast::Vector>()) {
       if (auto* el = Type(t->type())) {
         return builder_->create<sem::Vector>(const_cast<sem::Type*>(el),
@@ -341,14 +334,11 @@
     }
     if (auto* t = ty->As<ast::StorageTexture>()) {
       if (auto* el = Type(t->type())) {
-        if (!current_access_control_) {
-          diagnostics_.add_error("storage textures must have access control",
-                                 t->source());
+        if (!ValidateStorageTexture(t)) {
           return nullptr;
         }
         return builder_->create<sem::StorageTexture>(
-            t->dim(), t->image_format(),
-            current_access_control_->access_control(),
+            t->dim(), t->image_format(), t->access(),
             const_cast<sem::Type*>(el));
       }
       return nullptr;
@@ -377,6 +367,39 @@
   return s;
 }
 
+bool Resolver::ValidateStorageTexture(const ast::StorageTexture* t) {
+  switch (t->access()) {
+    case ast::Access::kUndefined:
+      diagnostics_.add_error("storage textures must have access control",
+                             t->source());
+      return false;
+    case ast::Access::kReadWrite:
+      diagnostics_.add_error(
+          "storage textures only support read-only and write-only access",
+          t->source());
+      return false;
+
+    case ast::Access::kRead:
+    case ast::Access::kWrite:
+      break;
+  }
+
+  if (!IsValidStorageTextureDimension(t->dim())) {
+    diagnostics_.add_error(
+        "cube dimensions for storage textures are not supported", t->source());
+    return false;
+  }
+
+  if (!IsValidStorageTextureImageFormat(t->image_format())) {
+    diagnostics_.add_error(
+        "image format must be one of the texel formats specified for storage "
+        "textues in https://gpuweb.github.io/gpuweb/wgsl/#texel-formats",
+        t->source());
+    return false;
+  }
+  return true;
+}
+
 Resolver::VariableInfo* Resolver::Variable(ast::Variable* var,
                                            VariableKind kind) {
   if (variable_to_info_.count(var)) {
@@ -466,35 +489,17 @@
     return nullptr;
   }
 
-  // TODO(crbug.com/tint/802): Temporary while ast::AccessControl exits.
-  auto find_first_access_control =
-      [this](const ast::Type* ty) -> const ast::AccessControl* {
-    if (ty == nullptr) {
-      return nullptr;
-    }
-    if (const ast::AccessControl* ac = ty->As<ast::AccessControl>()) {
-      return ac;
-    }
-    while (auto* tn = ty->As<ast::TypeName>()) {
-      auto it = named_type_info_.find(tn->name());
-      if (it == named_type_info_.end()) {
-        break;
-      }
-      auto* alias = it->second.ast->As<ast::Alias>();
-      if (!alias) {
-        break;
-      }
-      ty = alias->type();
-      if (auto* ac = ty->As<ast::AccessControl>()) {
-        return ac;
-      }
-    }
-    return nullptr;
-  };
+  auto access = var->declared_access();
+  if (access == ast::Access::kUndefined &&
+      storage_class == ast::StorageClass::kStorage) {
+    // https://gpuweb.github.io/gpuweb/wgsl/#access-mode-defaults
+    // For the storage storage class, the access mode is optional, and defaults
+    // to read.
+    access = ast::Access::kRead;
+  }
 
-  auto* access_control = find_first_access_control(var->type());
   auto* info = variable_infos_.Create(var, const_cast<sem::Type*>(type),
-                                      type_name, storage_class, access_control);
+                                      type_name, storage_class, access);
   variable_to_info_.emplace(var, info);
 
   return info;
@@ -658,25 +663,31 @@
       }
   }
 
+  // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
+  // The access mode always has a default, and except for variables in the
+  // storage storage class, must not be written.
+  if (info->storage_class != ast::StorageClass::kStorage &&
+      info->declaration->declared_access() != ast::Access::kUndefined) {
+    diagnostics_.add_error(
+        "variables declared not declared in the <storage> storage class must "
+        "not declare an access control",
+        info->declaration->source());
+    return false;
+  }
+
   switch (info->storage_class) {
     case ast::StorageClass::kStorage: {
-      // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
-      // Variables in the storage storage class and variables with a storage
-      // texture type must have an access attribute applied to the store type.
-
       // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
       // A variable in the storage storage class is a storage buffer variable.
       // Its store type must be a host-shareable structure type with block
       // attribute, satisfying the storage class constraints.
 
-      auto* str = info->access_control
-                      ? info->type->UnwrapRef()->As<sem::Struct>()
-                      : nullptr;
+      auto* str = info->type->UnwrapRef()->As<sem::Struct>();
 
       if (!str) {
         diagnostics_.add_error(
-            "variables declared in the <storage> storage class must be of an "
-            "[[access]] qualified structure type",
+            "variables declared in the <storage> storage class must be of a "
+            "structure type",
             info->declaration->source());
         return false;
       }
@@ -756,33 +767,6 @@
     }
   }
 
-  if (auto* storage_tex = info->type->UnwrapRef()->As<sem::StorageTexture>()) {
-    if (info->access_control->access_control() ==
-        ast::AccessControl::kReadWrite) {
-      diagnostics_.add_error(
-          "storage textures only support read-only and write-only access",
-          var->source());
-      return false;
-    }
-
-    if (!IsValidStorageTextureDimension(storage_tex->dim())) {
-      diagnostics_.add_error(
-          "cube dimensions for storage textures are not "
-          "supported",
-          var->source());
-      return false;
-    }
-
-    if (!IsValidStorageTextureImageFormat(storage_tex->image_format())) {
-      diagnostics_.add_error(
-          "image format must be one of the texel formats specified for "
-          "storage textues in "
-          "https://gpuweb.github.io/gpuweb/wgsl/#texel-formats",
-          var->source());
-      return false;
-    }
-  }
-
   if (storage_type->is_handle() &&
       var->declared_storage_class() != ast::StorageClass::kNone) {
     // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
@@ -2584,9 +2568,7 @@
       sem_var = builder_->create<sem::Variable>(var, info->type, constant_id);
     } else {
       sem_var = builder_->create<sem::Variable>(
-          var, info->type, info->storage_class,
-          info->access_control ? info->access_control->access_control()
-                               : ast::AccessControl::kReadWrite);
+          var, info->type, info->storage_class, info->access);
     }
 
     std::vector<const sem::VariableUser*> users;
@@ -3274,12 +3256,12 @@
                                      sem::Type* ty,
                                      const std::string& tn,
                                      ast::StorageClass sc,
-                                     const ast::AccessControl* ac)
+                                     ast::Access ac)
     : declaration(decl),
       type(ty),
       type_name(tn),
       storage_class(sc),
-      access_control(ac) {}
+      access(ac) {}
 
 Resolver::VariableInfo::~VariableInfo() = default;
 
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index 2efdf7c..4b893bf 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -89,14 +89,14 @@
                  sem::Type* type,
                  const std::string& type_name,
                  ast::StorageClass storage_class,
-                 const ast::AccessControl* ac);
+                 ast::Access ac);
     ~VariableInfo();
 
     ast::Variable const* const declaration;
     sem::Type* type;
     std::string const type_name;
     ast::StorageClass storage_class;
-    ast::AccessControl const* const access_control;
+    ast::Access const access;
     std::vector<ast::IdentifierExpression*> users;
     sem::BindingPoint binding_point;
   };
@@ -255,6 +255,7 @@
                                  const sem::Matrix* matrix_type);
   bool ValidateParameter(const VariableInfo* info);
   bool ValidateReturn(const ast::ReturnStatement* ret);
+  bool ValidateStorageTexture(const ast::StorageTexture* t);
   bool ValidateStructure(const sem::Struct* str);
   bool ValidateSwitch(const ast::SwitchStatement* s);
   bool ValidateVariable(const VariableInfo* info);
@@ -394,7 +395,6 @@
 
   FunctionInfo* current_function_ = nullptr;
   sem::Statement* current_statement_ = nullptr;
-  const ast::AccessControl* current_access_control_ = nullptr;
   BlockAllocator<VariableInfo> variable_infos_;
   BlockAllocator<FunctionInfo> function_infos_;
 };
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 84935c4..ba3ef08 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -784,15 +784,15 @@
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
   auto* s = Structure("S", {Member("m", ty.u32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
 
   auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
   auto* out_var = Global("out_var", ty.f32(), ast::StorageClass::kOutput);
-  auto* sb_var = Global("sb_var", a, ast::StorageClass::kStorage,
-                        ast::DecorationList{
-                            create<ast::BindingDecoration>(0),
-                            create<ast::GroupDecoration>(0),
-                        });
+  auto* sb_var =
+      Global("sb_var", s, ast::StorageClass::kStorage, ast::Access::kRead,
+             ast::DecorationList{
+                 create<ast::BindingDecoration>(0),
+                 create<ast::GroupDecoration>(0),
+             });
   auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
   auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
@@ -823,15 +823,15 @@
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
   auto* s = Structure("S", {Member("m", ty.u32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
 
   auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
   auto* out_var = Global("out_var", ty.f32(), ast::StorageClass::kOutput);
-  auto* sb_var = Global("sb_var", a, ast::StorageClass::kStorage,
-                        ast::DecorationList{
-                            create<ast::BindingDecoration>(0),
-                            create<ast::GroupDecoration>(0),
-                        });
+  auto* sb_var =
+      Global("sb_var", s, ast::StorageClass::kStorage, ast::Access::kRead,
+             ast::DecorationList{
+                 create<ast::BindingDecoration>(0),
+                 create<ast::GroupDecoration>(0),
+             });
   auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
   auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
@@ -1757,8 +1757,7 @@
 
 TEST_F(ResolverTest, StorageClass_SetForTexture) {
   auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
-  auto* ac = ty.access(ast::AccessControl::kRead, t);
-  auto* var = Global("var", ac,
+  auto* var = Global("var", t,
                      ast::DecorationList{
                          create<ast::BindingDecoration>(0),
                          create<ast::GroupDecoration>(0),
@@ -1780,6 +1779,22 @@
   EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kNone);
 }
 
+TEST_F(ResolverTest, Access_SetForStorageBuffer) {
+  // [[block]] struct S { x : i32 };
+  // var<storage> g : S;
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
+                      {create<ast::StructBlockDecoration>()});
+  auto* var = Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage,
+                     ast::DecorationList{
+                         create<ast::BindingDecoration>(0),
+                         create<ast::GroupDecoration>(0),
+                     });
+
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+  EXPECT_EQ(Sem().Get(var)->Access(), ast::Access::kRead);
+}
+
 TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
   // fn b() {}
   // fn c() { b(); }
diff --git a/src/resolver/resolver_test_helper.h b/src/resolver/resolver_test_helper.h
index 675fa33..3597e27 100644
--- a/src/resolver/resolver_test_helper.h
+++ b/src/resolver/resolver_test_helper.h
@@ -226,12 +226,6 @@
   return ty.builder->create<ast::TypeName>(name);
 }
 
-template <create_ast_type_func_ptr create_type>
-ast::Type* ast_access(const ProgramBuilder::TypesBuilder& ty) {
-  auto* type = create_type(ty);
-  return ty.access(ast::AccessControl::kRead, type);
-}
-
 inline sem::Type* sem_bool(const ProgramBuilder::TypesBuilder& ty) {
   return ty.builder->create<sem::Bool>();
 }
diff --git a/src/resolver/storage_class_validation_test.cc b/src/resolver/storage_class_validation_test.cc
index 67efdd7..29c8945 100644
--- a/src/resolver/storage_class_validation_test.cc
+++ b/src/resolver/storage_class_validation_test.cc
@@ -46,7 +46,7 @@
 
   EXPECT_EQ(
       r()->error(),
-      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+      R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
@@ -62,15 +62,15 @@
 
   EXPECT_EQ(
       r()->error(),
-      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+      R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
-  // var<storage> g : [[access(read)]] array<S, 3>;
+  // var<storage, read> g : array<S, 3>;
   auto* s = Structure("S", {Member("a", ty.f32())});
   auto* a = ty.array(s, 3);
-  auto* ac = ty.access(ast::AccessControl::kRead, a);
-  Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kStorage,
+  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -80,12 +80,12 @@
 
   EXPECT_EQ(
       r()->error(),
-      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+      R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
   // type a = bool;
-  // var<storage> g : [[access(read)]] a;
+  // var<storage, read> g : a;
   auto* a = ty.alias("a", ty.bool_());
   AST().AddConstructedType(a);
   Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
@@ -98,31 +98,15 @@
 
   EXPECT_EQ(
       r()->error(),
-      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
-}
-
-TEST_F(ResolverStorageClassValidationTest, StorageBufferNoAccessControl) {
-  // var<storage> g : S;
-  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
-  Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage,
-         ast::DecorationList{
-             create<ast::BindingDecoration>(0),
-             create<ast::GroupDecoration>(0),
-         });
-
-  ASSERT_FALSE(r()->Resolve());
-
-  EXPECT_EQ(
-      r()->error(),
-      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+      R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) {
   // struct S { x : i32 };
-  // var<storage> g : [[access(read)]] S;
+  // var<storage, read> g : S;
   auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
+  Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -138,11 +122,11 @@
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
   // [[block]] struct S { x : i32 };
-  // var<storage> g : [[access(read)]] S;
+  // var<storage, read> g : S;
   auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
+  Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -154,16 +138,15 @@
 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
   // [[block]] struct S { x : i32 };
   // type a1 = S;
-  // type a2 = [[access(read)]] a1;
-  // var<storage> g : a2;
+  // var<storage, read> g : a1;
   auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
                       {create<ast::StructBlockDecoration>()});
   auto* a1 = ty.alias("a1", s);
   AST().AddConstructedType(a1);
-  auto* ac = ty.access(ast::AccessControl::kRead, a1);
-  auto* a2 = ty.alias("a2", ac);
+  auto* a2 = ty.alias("a2", a1);
   AST().AddConstructedType(a2);
   Global(Source{{56, 78}}, "g", a2, ast::StorageClass::kStorage,
+         ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -206,11 +189,10 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
-  // var<uniform> g : [[access(read)]] array<S, 3>;
+  // var<uniform> g : array<S, 3>;
   auto* s = Structure("S", {Member("a", ty.f32())});
   auto* a = ty.array(s, 3);
-  auto* ac = ty.access(ast::AccessControl::kRead, a);
-  Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kUniform,
+  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -225,7 +207,7 @@
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
   // type a = bool;
-  // var<uniform> g : [[access(read)]] a;
+  // var<uniform> g : a;
   auto* a = ty.alias("a", ty.bool_());
   AST().AddConstructedType(a);
   Global(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
diff --git a/src/resolver/struct_storage_class_use_test.cc b/src/resolver/struct_storage_class_use_test.cc
index 46fe48d..7a83ab0 100644
--- a/src/resolver/struct_storage_class_use_test.cc
+++ b/src/resolver/struct_storage_class_use_test.cc
@@ -172,13 +172,12 @@
 TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
   auto* s = Structure("S", {Member("a", ty.f32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
   Global("x", s, ast::StorageClass::kUniform,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
          });
-  Global("y", ac, ast::StorageClass::kStorage,
+  Global("y", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(0),
diff --git a/src/resolver/type_validation_test.cc b/src/resolver/type_validation_test.cc
index 38375f8..c7882c0 100644
--- a/src/resolver/type_validation_test.cc
+++ b/src/resolver/type_validation_test.cc
@@ -66,10 +66,10 @@
 
 TEST_F(ResolverTypeValidationTest, GlobalConstantWithStorageClass_Fail) {
   // const<in> global_var: f32;
-  AST().AddGlobalVariable(
-      create<ast::Variable>(Source{{12, 34}}, Symbols().Register("global_var"),
-                            ast::StorageClass::kInput, ty.f32(), true,
-                            Expr(1.23f), ast::DecorationList{}));
+  AST().AddGlobalVariable(create<ast::Variable>(
+      Source{{12, 34}}, Symbols().Register("global_var"),
+      ast::StorageClass::kInput, ast::Access::kUndefined, ty.f32(), true,
+      Expr(1.23f), ast::DecorationList{}));
 
   EXPECT_FALSE(r()->Resolve());
   EXPECT_EQ(r()->error(),
@@ -393,11 +393,9 @@
     Params{ast_alias<ast_alias<ast_mat3x3<ast_alias<ast_alias<ast_f32>>>>>,
            sem_mat3x3<sem_f32>},
 
-    Params{ast_alias<ast_access<ast_alias<ast_bool>>>, sem_bool},
-    Params{ast_alias<ast_access<ast_alias<ast_vec3<ast_access<ast_f32>>>>>,
-           sem_vec3<sem_f32>},
-    Params{ast_alias<ast_access<ast_alias<ast_mat3x3<ast_access<ast_f32>>>>>,
-           sem_mat3x3<sem_f32>},
+    Params{ast_alias<ast_alias<ast_bool>>, sem_bool},
+    Params{ast_alias<ast_alias<ast_vec3<ast_f32>>>, sem_vec3<sem_f32>},
+    Params{ast_alias<ast_alias<ast_mat3x3<ast_f32>>>, sem_mat3x3<sem_f32>},
 };
 
 using CanonicalTest = ResolverTestWithParam<Params>;
@@ -526,13 +524,13 @@
 using StorageTextureDimensionTest = ResolverTestWithParam<DimensionParams>;
 TEST_P(StorageTextureDimensionTest, All) {
   // [[group(0), binding(0)]]
-  // var a : [[access(read)]] texture_storage_*<ru32int>;
+  // var a : texture_storage_*<ru32int, read>;
   auto& params = GetParam();
 
-  auto* st = ty.storage_texture(params.dim, ast::ImageFormat::kR32Uint);
-  auto* ac = ty.access(ast::AccessControl::kRead, st);
+  auto* st = ty.storage_texture(Source{{12, 34}}, params.dim,
+                                ast::ImageFormat::kR32Uint, ast::Access::kRead);
 
-  Global(Source{{12, 34}}, "a", ac, ast::StorageClass::kNone, nullptr,
+  Global("a", st, ast::StorageClass::kNone,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -597,42 +595,41 @@
 TEST_P(StorageTextureFormatTest, All) {
   auto& params = GetParam();
   // [[group(0), binding(0)]]
-  // var a : [[access(read)]] texture_storage_1d<*>;
+  // var a : texture_storage_1d<*, read>;
   // [[group(0), binding(1)]]
-  // var b : [[access(read)]] texture_storage_2d<*>;
+  // var b : texture_storage_2d<*, read>;
   // [[group(0), binding(2)]]
-  // var c : [[access(read)]] texture_storage_2d_array<*>;
+  // var c : texture_storage_2d_array<*, read>;
   // [[group(0), binding(3)]]
-  // var d : [[access(read)]] texture_storage_3d<*>;
+  // var d : texture_storage_3d<*, read>;
 
-  auto* st_a = ty.storage_texture(ast::TextureDimension::k1d, params.format);
-  auto* ac_a = ty.access(ast::AccessControl::kRead, st_a);
-  Global(Source{{12, 34}}, "a", ac_a, ast::StorageClass::kNone, nullptr,
+  auto* st_a = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
+                                  params.format, ast::Access::kRead);
+  Global("a", st_a, ast::StorageClass::kNone,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
          });
 
-  auto* st_b = ty.storage_texture(ast::TextureDimension::k2d, params.format);
-  auto* ac_b = ty.access(ast::AccessControl::kRead, st_b);
-  Global("b", ac_b, ast::StorageClass::kNone, nullptr,
+  auto* st_b = ty.storage_texture(ast::TextureDimension::k2d, params.format,
+                                  ast::Access::kRead);
+  Global("b", st_b, ast::StorageClass::kNone,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
          });
 
-  auto* st_c =
-      ty.storage_texture(ast::TextureDimension::k2dArray, params.format);
-  auto* ac_c = ty.access(ast::AccessControl::kRead, st_c);
-  Global("c", ac_c, ast::StorageClass::kNone, nullptr,
+  auto* st_c = ty.storage_texture(ast::TextureDimension::k2dArray,
+                                  params.format, ast::Access::kRead);
+  Global("c", st_c, ast::StorageClass::kNone,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(2),
          });
 
-  auto* st_d = ty.storage_texture(ast::TextureDimension::k3d, params.format);
-  auto* ac_d = ty.access(ast::AccessControl::kRead, st_d);
-  Global("d", ac_d, ast::StorageClass::kNone, nullptr,
+  auto* st_d = ty.storage_texture(ast::TextureDimension::k3d, params.format,
+                                  ast::Access::kRead);
+  Global("d", st_d, ast::StorageClass::kNone,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(3),
@@ -652,16 +649,17 @@
                          StorageTextureFormatTest,
                          testing::ValuesIn(format_cases));
 
-using StorageTextureAccessControlTest = ResolverTest;
+using StorageTextureAccessTest = ResolverTest;
 
-TEST_F(StorageTextureAccessControlTest, MissingAccessControl_Fail) {
+TEST_F(StorageTextureAccessTest, MissingAccess_Fail) {
   // [[group(0), binding(0)]]
   // var a : texture_storage_1d<ru32int>;
 
-  auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
-                                ast::ImageFormat::kR32Uint);
+  auto* st =
+      ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
+                         ast::ImageFormat::kR32Uint, ast::Access::kUndefined);
 
-  Global("a", st, ast::StorageClass::kNone, nullptr,
+  Global("a", st, ast::StorageClass::kNone,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -672,15 +670,15 @@
             "12:34 error: storage textures must have access control");
 }
 
-TEST_F(StorageTextureAccessControlTest, RWAccessControl_Fail) {
+TEST_F(StorageTextureAccessTest, RWAccess_Fail) {
   // [[group(0), binding(0)]]
-  // var a : [[access(readwrite)]] texture_storage_1d<ru32int>;
+  // var a : texture_storage_1d<ru32int, read_write>;
 
-  auto* st = ty.storage_texture(ast::TextureDimension::k1d,
-                                ast::ImageFormat::kR32Uint);
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, st);
+  auto* st =
+      ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
+                         ast::ImageFormat::kR32Uint, ast::Access::kReadWrite);
 
-  Global(Source{{12, 34}}, "a", ac, ast::StorageClass::kNone, nullptr,
+  Global("a", st, ast::StorageClass::kNone, nullptr,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -692,15 +690,14 @@
             "write-only access");
 }
 
-TEST_F(StorageTextureAccessControlTest, ReadOnlyAccessControl_Pass) {
+TEST_F(StorageTextureAccessTest, ReadOnlyAccess_Pass) {
   // [[group(0), binding(0)]]
-  // var a : [[access(read)]] texture_storage_1d<ru32int>;
+  // var a : texture_storage_1d<ru32int, read>;
 
   auto* st = ty.storage_texture(ast::TextureDimension::k1d,
-                                ast::ImageFormat::kR32Uint);
-  auto* ac = ty.access(ast::AccessControl::kRead, st);
+                                ast::ImageFormat::kR32Uint, ast::Access::kRead);
 
-  Global("a", ac, ast::StorageClass::kNone, nullptr,
+  Global("a", st, ast::StorageClass::kNone, nullptr,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -709,15 +706,15 @@
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(StorageTextureAccessControlTest, WriteOnlyAccessControl_Pass) {
+TEST_F(StorageTextureAccessTest, WriteOnlyAccess_Pass) {
   // [[group(0), binding(0)]]
-  // var a : [[access(write)]] texture_storage_1d<ru32int>;
+  // var a : texture_storage_1d<ru32int, write>;
 
-  auto* st = ty.storage_texture(ast::TextureDimension::k1d,
-                                ast::ImageFormat::kR32Uint);
-  auto* ac = ty.access(ast::AccessControl::kWrite, st);
+  auto* st =
+      ty.storage_texture(ast::TextureDimension::k1d, ast::ImageFormat::kR32Uint,
+                         ast::Access::kWrite);
 
-  Global("a", ac, ast::StorageClass::kNone, nullptr,
+  Global("a", st, ast::StorageClass::kNone, nullptr,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/sem/storage_texture_type.cc b/src/sem/storage_texture_type.cc
index ab6e270..d690354 100644
--- a/src/sem/storage_texture_type.cc
+++ b/src/sem/storage_texture_type.cc
@@ -23,12 +23,9 @@
 
 StorageTexture::StorageTexture(ast::TextureDimension dim,
                                ast::ImageFormat format,
-                               ast::AccessControl::Access access_control,
+                               ast::Access access,
                                sem::Type* subtype)
-    : Base(dim),
-      image_format_(format),
-      access_control_(access_control),
-      subtype_(subtype) {}
+    : Base(dim), image_format_(format), access_(access), subtype_(subtype) {}
 
 StorageTexture::StorageTexture(StorageTexture&&) = default;
 
@@ -37,14 +34,14 @@
 std::string StorageTexture::type_name() const {
   std::ostringstream out;
   out << "__storage_texture_" << dim() << "_" << image_format_ << "_"
-      << access_control_;
+      << access_;
   return out.str();
 }
 
 std::string StorageTexture::FriendlyName(const SymbolTable&) const {
   std::ostringstream out;
-  out << "texture_storage_" << dim() << "<" << image_format_ << ", "
-      << access_control_ << ">";
+  out << "texture_storage_" << dim() << "<" << image_format_ << ", " << access_
+      << ">";
   return out.str();
 }
 
diff --git a/src/sem/storage_texture_type.h b/src/sem/storage_texture_type.h
index 3a36743..216ed28 100644
--- a/src/sem/storage_texture_type.h
+++ b/src/sem/storage_texture_type.h
@@ -17,7 +17,7 @@
 
 #include <string>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/storage_texture.h"
 #include "src/sem/texture_type.h"
 
@@ -32,11 +32,11 @@
   /// Constructor
   /// @param dim the dimensionality of the texture
   /// @param format the image format of the texture
-  /// @param access_control the access control type of the texture
+  /// @param access the access control type of the texture
   /// @param subtype the storage subtype. Use SubtypeFor() to calculate this.
   StorageTexture(ast::TextureDimension dim,
                  ast::ImageFormat format,
-                 ast::AccessControl::Access access_control,
+                 ast::Access access,
                  sem::Type* subtype);
 
   /// Move constructor
@@ -50,7 +50,7 @@
   ast::ImageFormat image_format() const { return image_format_; }
 
   /// @returns the access control
-  ast::AccessControl::Access access_control() const { return access_control_; }
+  ast::Access access() const { return access_; }
 
   /// @returns the name for this type
   std::string type_name() const override;
@@ -67,7 +67,7 @@
 
  private:
   ast::ImageFormat const image_format_;
-  ast::AccessControl::Access const access_control_;
+  ast::Access const access_;
   Type* const subtype_;
 };
 
diff --git a/src/sem/storage_texture_type_test.cc b/src/sem/storage_texture_type_test.cc
index 80a078f..8917af0 100644
--- a/src/sem/storage_texture_type_test.cc
+++ b/src/sem/storage_texture_type_test.cc
@@ -30,7 +30,7 @@
       StorageTexture::SubtypeFor(ast::ImageFormat::kRgba32Float, Types());
   auto* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRgba32Float,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
   EXPECT_EQ(s->dim(), ast::TextureDimension::k2dArray);
 }
 
@@ -39,7 +39,7 @@
       StorageTexture::SubtypeFor(ast::ImageFormat::kRgba32Float, Types());
   auto* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRgba32Float,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
   EXPECT_EQ(s->image_format(), ast::ImageFormat::kRgba32Float);
 }
 
@@ -48,7 +48,7 @@
       StorageTexture::SubtypeFor(ast::ImageFormat::kRgba32Float, Types());
   auto* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRgba32Float,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
   EXPECT_EQ(s->type_name(),
             "__storage_texture_2d_array_rgba32float_read_write");
 }
@@ -58,7 +58,7 @@
       StorageTexture::SubtypeFor(ast::ImageFormat::kRgba32Float, Types());
   auto* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRgba32Float,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
   EXPECT_EQ(s->FriendlyName(Symbols()),
             "texture_storage_2d_array<rgba32float, read_write>");
 }
@@ -68,7 +68,7 @@
       sem::StorageTexture::SubtypeFor(ast::ImageFormat::kRgba32Float, Types());
   Type* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRgba32Float,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
 
   auto program = Build();
 
@@ -83,7 +83,7 @@
       sem::StorageTexture::SubtypeFor(ast::ImageFormat::kRg32Uint, Types());
   Type* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRg32Uint,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
 
   auto program = Build();
 
@@ -98,7 +98,7 @@
       sem::StorageTexture::SubtypeFor(ast::ImageFormat::kRgba32Sint, Types());
   Type* s = create<StorageTexture>(ast::TextureDimension::k2dArray,
                                    ast::ImageFormat::kRgba32Sint,
-                                   ast::AccessControl::kReadWrite, subtype);
+                                   ast::Access::kReadWrite, subtype);
 
   auto program = Build();
 
diff --git a/src/sem/variable.cc b/src/sem/variable.cc
index a8cd011..305b4ac 100644
--- a/src/sem/variable.cc
+++ b/src/sem/variable.cc
@@ -26,11 +26,11 @@
 Variable::Variable(const ast::Variable* declaration,
                    const sem::Type* type,
                    ast::StorageClass storage_class,
-                   ast::AccessControl::Access access_control)
+                   ast::Access access)
     : declaration_(declaration),
       type_(type),
       storage_class_(storage_class),
-      access_control_(access_control),
+      access_(access),
       is_pipeline_constant_(false) {}
 
 Variable::Variable(const ast::Variable* declaration,
@@ -39,7 +39,7 @@
     : declaration_(declaration),
       type_(type),
       storage_class_(ast::StorageClass::kNone),
-      access_control_(ast::AccessControl::kReadWrite),
+      access_(ast::Access::kReadWrite),
       is_pipeline_constant_(true),
       constant_id_(constant_id) {}
 
diff --git a/src/sem/variable.h b/src/sem/variable.h
index 2b2b2e7..ebe9c49 100644
--- a/src/sem/variable.h
+++ b/src/sem/variable.h
@@ -17,7 +17,7 @@
 
 #include <vector>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/storage_class.h"
 #include "src/sem/expression.h"
 
@@ -42,11 +42,11 @@
   /// @param declaration the AST declaration node
   /// @param type the variable type
   /// @param storage_class the variable storage class
-  /// @param access_control the variable access control type
+  /// @param access the variable access control type
   Variable(const ast::Variable* declaration,
            const sem::Type* type,
            ast::StorageClass storage_class,
-           ast::AccessControl::Access access_control);
+           ast::Access access);
 
   /// Constructor for overridable pipeline constants
   /// @param declaration the AST declaration node
@@ -69,7 +69,7 @@
   ast::StorageClass StorageClass() const { return storage_class_; }
 
   /// @returns the access control for the variable
-  ast::AccessControl::Access AccessControl() const { return access_control_; }
+  ast::Access Access() const { return access_; }
 
   /// @returns the expressions that use the variable
   const std::vector<const VariableUser*>& Users() const { return users_; }
@@ -87,7 +87,7 @@
   const ast::Variable* const declaration_;
   const sem::Type* const type_;
   ast::StorageClass const storage_class_;
-  ast::AccessControl::Access const access_control_;
+  ast::Access const access_;
   std::vector<const VariableUser*> users_;
   const bool is_pipeline_constant_;
   const uint16_t constant_id_ = 0;
diff --git a/src/transform/binding_remapper.cc b/src/transform/binding_remapper.cc
index f89bfd9..3755fff 100644
--- a/src/transform/binding_remapper.cc
+++ b/src/transform/binding_remapper.cc
@@ -112,13 +112,12 @@
       // Replace any access controls.
       auto ac_it = remappings->access_controls.find(from);
       if (ac_it != remappings->access_controls.end()) {
-        ast::AccessControl::Access ac = ac_it->second;
+        ast::Access ac = ac_it->second;
         auto* ty = in->Sem().Get(var)->Type()->UnwrapRef();
         ast::Type* inner_ty = CreateASTTypeFor(&ctx, ty);
-        auto* new_ty = ctx.dst->create<ast::AccessControl>(ac, inner_ty);
         auto* new_var = ctx.dst->create<ast::Variable>(
             ctx.Clone(var->source()), ctx.Clone(var->symbol()),
-            var->declared_storage_class(), new_ty, var->is_const(),
+            var->declared_storage_class(), ac, inner_ty, var->is_const(),
             ctx.Clone(var->constructor()), ctx.Clone(var->decorations()));
         ctx.Replace(var, new_var);
       }
diff --git a/src/transform/binding_remapper.h b/src/transform/binding_remapper.h
index 81efa25..b271c1f 100644
--- a/src/transform/binding_remapper.h
+++ b/src/transform/binding_remapper.h
@@ -17,7 +17,7 @@
 
 #include <unordered_map>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/sem/binding_point.h"
 #include "src/transform/transform.h"
 
@@ -35,8 +35,7 @@
   using BindingPoints = std::unordered_map<BindingPoint, BindingPoint>;
 
   /// AccessControls is a map of old binding point to new access control
-  using AccessControls =
-      std::unordered_map<BindingPoint, ast::AccessControl::Access>;
+  using AccessControls = std::unordered_map<BindingPoint, ast::Access>;
 
   /// Remappings is consumed by the BindingRemapper transform.
   /// Data holds information about shader usage and constant buffer offsets.
diff --git a/src/transform/binding_remapper_test.cc b/src/transform/binding_remapper_test.cc
index 2015a01..ed8ce0a 100644
--- a/src/transform/binding_remapper_test.cc
+++ b/src/transform/binding_remapper_test.cc
@@ -30,9 +30,9 @@
 struct S {
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
 [[stage(compute)]]
 fn f() {
@@ -55,9 +55,9 @@
 struct S {
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
 [[stage(compute)]]
 fn f() {
@@ -69,9 +69,9 @@
 struct S {
 };
 
-[[group(1), binding(2)]] var<storage> a : [[access(read)]] S;
+[[group(1), binding(2)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
 [[stage(compute)]]
 fn f() {
@@ -97,11 +97,11 @@
 struct S {
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(write)]] S;
+[[group(3), binding(2)]] var<storage, write> b : S;
 
-[[group(4), binding(3)]] var<storage> c : [[access(read)]] S;
+[[group(4), binding(3)]] var<storage, read> c : S;
 
 [[stage(compute)]]
 fn f() {
@@ -113,11 +113,11 @@
 struct S {
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(write)]] S;
+[[group(2), binding(1)]] var<storage, write> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(write)]] S;
+[[group(3), binding(2)]] var<storage, write> b : S;
 
-[[group(4), binding(3)]] var<storage> c : [[access(read)]] S;
+[[group(4), binding(3)]] var<storage, read> c : S;
 
 [[stage(compute)]]
 fn f() {
@@ -128,9 +128,9 @@
   data.Add<BindingRemapper::Remappings>(
       BindingRemapper::BindingPoints{},
       BindingRemapper::AccessControls{
-          {{2, 1}, ast::AccessControl::kWrite},  // Modify access control
+          {{2, 1}, ast::Access::kWrite},  // Modify access control
           // Keep [[group(3), binding(2)]] as is
-          {{4, 3}, ast::AccessControl::kRead},  // Add access control
+          {{4, 3}, ast::Access::kRead},  // Add access control
       });
   auto got = Run<BindingRemapper>(src, data);
 
@@ -145,9 +145,9 @@
 struct S {
 };
 
-type ReadOnlyS = [[access(read)]] S;
+type, read ReadOnlyS = S;
 
-type WriteOnlyS = [[access(write)]] S;
+type, write WriteOnlyS = S;
 
 type A = S;
 
@@ -167,17 +167,17 @@
 struct S {
 };
 
-type ReadOnlyS = [[access(read)]] S;
+type, read ReadOnlyS = S;
 
-type WriteOnlyS = [[access(write)]] S;
+type, write WriteOnlyS = S;
 
 type A = S;
 
-[[group(2), binding(1)]] var<storage> a : [[access(write)]] S;
+[[group(2), binding(1)]] var<storage, write> a : S;
 
 [[group(3), binding(2)]] var<storage> b : WriteOnlyS;
 
-[[group(4), binding(3)]] var<storage> c : [[access(write)]] S;
+[[group(4), binding(3)]] var<storage, write> c : S;
 
 [[stage(compute)]]
 fn f() {
@@ -188,9 +188,9 @@
   data.Add<BindingRemapper::Remappings>(
       BindingRemapper::BindingPoints{},
       BindingRemapper::AccessControls{
-          {{2, 1}, ast::AccessControl::kWrite},  // Modify access control
+          {{2, 1}, ast::Access::kWrite},  // Modify access control
           // Keep [[group(3), binding(2)]] as is
-          {{4, 3}, ast::AccessControl::kRead},  // Add access control
+          {{4, 3}, ast::Access::kRead},  // Add access control
       });
   auto got = Run<BindingRemapper>(src, data);
 
@@ -203,9 +203,9 @@
 struct S {
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
 [[stage(compute)]]
 fn f() {
@@ -217,9 +217,9 @@
 struct S {
 };
 
-[[group(4), binding(5)]] var<storage> a : [[access(write)]] S;
+[[group(4), binding(5)]] var<storage, write> a : S;
 
-[[group(6), binding(7)]] var<storage> b : [[access(write)]] S;
+[[group(6), binding(7)]] var<storage, write> b : S;
 
 [[stage(compute)]]
 fn f() {
@@ -233,8 +233,8 @@
           {{3, 2}, {6, 7}},
       },
       BindingRemapper::AccessControls{
-          {{2, 1}, ast::AccessControl::kWrite},
-          {{3, 2}, ast::AccessControl::kWrite},
+          {{2, 1}, ast::Access::kWrite},
+          {{3, 2}, ast::Access::kWrite},
       });
   auto got = Run<BindingRemapper>(src, data);
 
@@ -248,13 +248,13 @@
   i : i32;
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
-[[group(4), binding(3)]] var<storage> c : [[access(read)]] S;
+[[group(4), binding(3)]] var<storage, read> c : S;
 
-[[group(5), binding(4)]] var<storage> d : [[access(read)]] S;
+[[group(5), binding(4)]] var<storage, read> d : S;
 
 [[stage(compute)]]
 fn f() {
@@ -268,13 +268,13 @@
   i : i32;
 };
 
-[[internal(disable_validation__binding_point_collision), group(1), binding(1)]] var<storage> a : [[access(read)]] S;
+[[internal(disable_validation__binding_point_collision), group(1), binding(1)]] var<storage, read> a : S;
 
-[[internal(disable_validation__binding_point_collision), group(1), binding(1)]] var<storage> b : [[access(read)]] S;
+[[internal(disable_validation__binding_point_collision), group(1), binding(1)]] var<storage, read> b : S;
 
-[[internal(disable_validation__binding_point_collision), group(5), binding(4)]] var<storage> c : [[access(read)]] S;
+[[internal(disable_validation__binding_point_collision), group(5), binding(4)]] var<storage, read> c : S;
 
-[[internal(disable_validation__binding_point_collision), group(5), binding(4)]] var<storage> d : [[access(read)]] S;
+[[internal(disable_validation__binding_point_collision), group(5), binding(4)]] var<storage, read> d : S;
 
 [[stage(compute)]]
 fn f() {
@@ -302,13 +302,13 @@
   i : i32;
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
 
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
-[[group(4), binding(3)]] var<storage> c : [[access(read)]] S;
+[[group(4), binding(3)]] var<storage, read> c : S;
 
-[[group(5), binding(4)]] var<storage> d : [[access(read)]] S;
+[[group(5), binding(4)]] var<storage, read> d : S;
 
 [[stage(compute)]]
 fn f1() {
@@ -327,13 +327,13 @@
   i : i32;
 };
 
-[[group(1), binding(1)]] var<storage> a : [[access(read)]] S;
+[[group(1), binding(1)]] var<storage, read> a : S;
 
-[[group(1), binding(1)]] var<storage> b : [[access(read)]] S;
+[[group(1), binding(1)]] var<storage, read> b : S;
 
-[[group(5), binding(4)]] var<storage> c : [[access(read)]] S;
+[[group(5), binding(4)]] var<storage, read> c : S;
 
-[[group(5), binding(4)]] var<storage> d : [[access(read)]] S;
+[[group(5), binding(4)]] var<storage, read> d : S;
 
 [[stage(compute)]]
 fn f1() {
@@ -365,8 +365,8 @@
 struct S {
 };
 
-[[group(2), binding(1)]] var<storage> a : [[access(read)]] S;
-[[group(3), binding(2)]] var<storage> b : [[access(read)]] S;
+[[group(2), binding(1)]] var<storage, read> a : S;
+[[group(3), binding(2)]] var<storage, read> b : S;
 
 [[stage(compute)]]
 fn f() {}
diff --git a/src/transform/bound_array_accessors_test.cc b/src/transform/bound_array_accessors_test.cc
index b9120d4..53e15d3 100644
--- a/src/transform/bound_array_accessors_test.cc
+++ b/src/transform/bound_array_accessors_test.cc
@@ -540,7 +540,7 @@
   a : f32;
   b : array<f32>;
 };
-[[group(0), binding(0)]] var<storage> s : [[access(read)]] S;
+[[group(0), binding(0)]] var<storage, read> s : S;
 
 fn f() {
   var d : f32 = s.b[25];
@@ -554,7 +554,7 @@
   b : array<f32>;
 };
 
-[[group(0), binding(0)]] var<storage> s : [[access(read)]] S;
+[[group(0), binding(0)]] var<storage, read> s : S;
 
 fn f() {
   var d : f32 = s.b[min(u32(25), (arrayLength(s.b) - 1u))];
@@ -601,7 +601,7 @@
   b : array<f32>;
 };
 
-[[group(0), binding(0)]] var<storage> s : [[access(read)]] S;
+[[group(0), binding(0)]] var<storage, read> s : S;
 
 let c : u32 = 1u;
 
@@ -619,7 +619,7 @@
   b : array<f32>;
 };
 
-[[group(0), binding(0)]] var<storage> s : [[access(read)]] S;
+[[group(0), binding(0)]] var<storage, read> s : S;
 
 let c : u32 = 1u;
 
diff --git a/src/transform/calculate_array_length.cc b/src/transform/calculate_array_length.cc
index e6becb4..19490df 100644
--- a/src/transform/calculate_array_length.cc
+++ b/src/transform/calculate_array_length.cc
@@ -91,7 +91,8 @@
               // in order for HLSL to emit this as a ByteAddressBuffer.
               ctx.dst->create<ast::Variable>(
                   ctx.dst->Sym("buffer"), ast::StorageClass::kStorage,
-                  buffer_typename, true, nullptr, ast::DecorationList{}),
+                  ast::Access::kUndefined, buffer_typename, true, nullptr,
+                  ast::DecorationList{}),
               ctx.dst->Param("result",
                              ctx.dst->ty.pointer(ctx.dst->ty.u32(),
                                                  ast::StorageClass::kFunction)),
diff --git a/src/transform/calculate_array_length_test.cc b/src/transform/calculate_array_length_test.cc
index 30083c3..557454b 100644
--- a/src/transform/calculate_array_length_test.cc
+++ b/src/transform/calculate_array_length_test.cc
@@ -30,7 +30,7 @@
   arr : array<i32>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -48,7 +48,7 @@
 [[internal(intrinsic_buffer_size)]]
 fn tint_symbol(buffer : SB, result : ptr<function, u32>)
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -72,7 +72,7 @@
   arr : array<i32>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -92,7 +92,7 @@
 [[internal(intrinsic_buffer_size)]]
 fn tint_symbol(buffer : SB, result : ptr<function, u32>)
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -119,7 +119,7 @@
   arr : [[stride(64)]] array<i32>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -138,7 +138,7 @@
 [[internal(intrinsic_buffer_size)]]
 fn tint_symbol(buffer : SB, result : ptr<function, u32>)
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -162,7 +162,7 @@
   arr : array<i32>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -186,7 +186,7 @@
 [[internal(intrinsic_buffer_size)]]
 fn tint_symbol(buffer : SB, result : ptr<function, u32>)
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read)]] SB;
+[[group(0), binding(0)]] var<storage, read> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -225,9 +225,9 @@
   arr2 : array<vec4<f32>>;
 };
 
-[[group(0), binding(0)]] var<storage> sb1 : [[access(read)]] SB1;
+[[group(0), binding(0)]] var<storage, read> sb1 : SB1;
 
-[[group(0), binding(1)]] var<storage> sb2 : [[access(read)]] SB2;
+[[group(0), binding(1)]] var<storage, read> sb2 : SB2;
 
 [[stage(compute)]]
 fn main() {
@@ -256,9 +256,9 @@
 [[internal(intrinsic_buffer_size)]]
 fn tint_symbol_3(buffer : SB2, result : ptr<function, u32>)
 
-[[group(0), binding(0)]] var<storage> sb1 : [[access(read)]] SB1;
+[[group(0), binding(0)]] var<storage, read> sb1 : SB1;
 
-[[group(0), binding(1)]] var<storage> sb2 : [[access(read)]] SB2;
+[[group(0), binding(1)]] var<storage, read> sb2 : SB2;
 
 [[stage(compute)]]
 fn main() {
diff --git a/src/transform/decompose_storage_access.cc b/src/transform/decompose_storage_access.cc
index e718b13..c89288c 100644
--- a/src/transform/decompose_storage_access.cc
+++ b/src/transform/decompose_storage_access.cc
@@ -360,17 +360,6 @@
   StorageBufferAccess target;            // The target for the write
 };
 
-ast::Type* MaybeCreateASTAccessControl(CloneContext* ctx,
-                                       const sem::VariableUser* var_user,
-                                       ast::Type* ty) {
-  if (var_user &&
-      var_user->Variable()->StorageClass() == ast::StorageClass::kStorage) {
-    return ctx->dst->create<ast::AccessControl>(
-        var_user->Variable()->AccessControl(), ty);
-  }
-  return ty;
-}
-
 }  // namespace
 
 /// State holds the current transform state
@@ -431,14 +420,14 @@
                   const sem::VariableUser* var_user) {
     return utils::GetOrCreate(load_funcs, TypePair{buf_ty, el_ty}, [&] {
       auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty);
-      buf_ast_ty = MaybeCreateASTAccessControl(&ctx, var_user, buf_ast_ty);
 
       ast::VariableList params = {
           // Note: The buffer parameter requires the kStorage StorageClass in
           // order for HLSL to emit this as a ByteAddressBuffer.
           ctx.dst->create<ast::Variable>(
-              ctx.dst->Sym("buffer"), ast::StorageClass::kStorage, buf_ast_ty,
-              true, nullptr, ast::DecorationList{}),
+              ctx.dst->Sym("buffer"), ast::StorageClass::kStorage,
+              var_user->Variable()->Access(), buf_ast_ty, true, nullptr,
+              ast::DecorationList{}),
           ctx.dst->Param("offset", ctx.dst->ty.u32()),
       };
 
@@ -507,14 +496,14 @@
                    const sem::VariableUser* var_user) {
     return utils::GetOrCreate(store_funcs, TypePair{buf_ty, el_ty}, [&] {
       auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty);
-      buf_ast_ty = MaybeCreateASTAccessControl(&ctx, var_user, buf_ast_ty);
       auto* el_ast_ty = CreateASTTypeFor(&ctx, el_ty);
       ast::VariableList params{
           // Note: The buffer parameter requires the kStorage StorageClass in
           // order for HLSL to emit this as a ByteAddressBuffer.
           ctx.dst->create<ast::Variable>(
-              ctx.dst->Sym("buffer"), ast::StorageClass::kStorage, buf_ast_ty,
-              true, nullptr, ast::DecorationList{}),
+              ctx.dst->Sym("buffer"), ast::StorageClass::kStorage,
+              var_user->Variable()->Access(), buf_ast_ty, true, nullptr,
+              ast::DecorationList{}),
           ctx.dst->Param("offset", ctx.dst->ty.u32()),
           ctx.dst->Param("value", el_ast_ty),
       };
diff --git a/src/transform/decompose_storage_access_test.cc b/src/transform/decompose_storage_access_test.cc
index 62fa75c..5d870f9 100644
--- a/src/transform/decompose_storage_access_test.cc
+++ b/src/transform/decompose_storage_access_test.cc
@@ -50,7 +50,7 @@
   v : array<vec3<f32>, 2>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -107,82 +107,82 @@
 };
 
 [[internal(intrinsic_load_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32) -> i32
+fn tint_symbol(buffer : SB, offset : u32) -> i32
 
 [[internal(intrinsic_load_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_1(buffer : [[access(read_write)]] SB, offset : u32) -> u32
+fn tint_symbol_1(buffer : SB, offset : u32) -> u32
 
 [[internal(intrinsic_load_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_2(buffer : [[access(read_write)]] SB, offset : u32) -> f32
+fn tint_symbol_2(buffer : SB, offset : u32) -> f32
 
 [[internal(intrinsic_load_vec2_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_3(buffer : [[access(read_write)]] SB, offset : u32) -> vec2<i32>
+fn tint_symbol_3(buffer : SB, offset : u32) -> vec2<i32>
 
 [[internal(intrinsic_load_vec2_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_4(buffer : [[access(read_write)]] SB, offset : u32) -> vec2<u32>
+fn tint_symbol_4(buffer : SB, offset : u32) -> vec2<u32>
 
 [[internal(intrinsic_load_vec2_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_5(buffer : [[access(read_write)]] SB, offset : u32) -> vec2<f32>
+fn tint_symbol_5(buffer : SB, offset : u32) -> vec2<f32>
 
 [[internal(intrinsic_load_vec3_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_6(buffer : [[access(read_write)]] SB, offset : u32) -> vec3<i32>
+fn tint_symbol_6(buffer : SB, offset : u32) -> vec3<i32>
 
 [[internal(intrinsic_load_vec3_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_7(buffer : [[access(read_write)]] SB, offset : u32) -> vec3<u32>
+fn tint_symbol_7(buffer : SB, offset : u32) -> vec3<u32>
 
 [[internal(intrinsic_load_vec3_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_8(buffer : [[access(read_write)]] SB, offset : u32) -> vec3<f32>
+fn tint_symbol_8(buffer : SB, offset : u32) -> vec3<f32>
 
 [[internal(intrinsic_load_vec4_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_9(buffer : [[access(read_write)]] SB, offset : u32) -> vec4<i32>
+fn tint_symbol_9(buffer : SB, offset : u32) -> vec4<i32>
 
 [[internal(intrinsic_load_vec4_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_10(buffer : [[access(read_write)]] SB, offset : u32) -> vec4<u32>
+fn tint_symbol_10(buffer : SB, offset : u32) -> vec4<u32>
 
 [[internal(intrinsic_load_vec4_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_11(buffer : [[access(read_write)]] SB, offset : u32) -> vec4<f32>
+fn tint_symbol_11(buffer : SB, offset : u32) -> vec4<f32>
 
-fn tint_symbol_12(buffer : [[access(read_write)]] SB, offset : u32) -> mat2x2<f32> {
+fn tint_symbol_12(buffer : SB, offset : u32) -> mat2x2<f32> {
   return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_13(buffer : [[access(read_write)]] SB, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_13(buffer : SB, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_14(buffer : [[access(read_write)]] SB, offset : u32) -> mat2x4<f32> {
+fn tint_symbol_14(buffer : SB, offset : u32) -> mat2x4<f32> {
   return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(buffer : [[access(read_write)]] SB, offset : u32) -> mat3x2<f32> {
+fn tint_symbol_15(buffer : SB, offset : u32) -> mat3x2<f32> {
   return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(buffer : [[access(read_write)]] SB, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_16(buffer : SB, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_17(buffer : [[access(read_write)]] SB, offset : u32) -> mat3x4<f32> {
+fn tint_symbol_17(buffer : SB, offset : u32) -> mat3x4<f32> {
   return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(buffer : [[access(read_write)]] SB, offset : u32) -> mat4x2<f32> {
+fn tint_symbol_18(buffer : SB, offset : u32) -> mat4x2<f32> {
   return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_19(buffer : [[access(read_write)]] SB, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_19(buffer : SB, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_20(buffer : [[access(read_write)]] SB, offset : u32) -> mat4x4<f32> {
+fn tint_symbol_20(buffer : SB, offset : u32) -> mat4x4<f32> {
   return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(buffer : [[access(read_write)]] SB, offset : u32) -> array<vec3<f32>, 2> {
+fn tint_symbol_21(buffer : SB, offset : u32) -> array<vec3<f32>, 2> {
   return array<vec3<f32>, 2>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -244,7 +244,7 @@
   v : array<vec3<f32>, 2>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -301,101 +301,101 @@
 };
 
 [[internal(intrinsic_store_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32, value : i32)
+fn tint_symbol(buffer : SB, offset : u32, value : i32)
 
 [[internal(intrinsic_store_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_1(buffer : [[access(read_write)]] SB, offset : u32, value : u32)
+fn tint_symbol_1(buffer : SB, offset : u32, value : u32)
 
 [[internal(intrinsic_store_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_2(buffer : [[access(read_write)]] SB, offset : u32, value : f32)
+fn tint_symbol_2(buffer : SB, offset : u32, value : f32)
 
 [[internal(intrinsic_store_vec2_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_3(buffer : [[access(read_write)]] SB, offset : u32, value : vec2<i32>)
+fn tint_symbol_3(buffer : SB, offset : u32, value : vec2<i32>)
 
 [[internal(intrinsic_store_vec2_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_4(buffer : [[access(read_write)]] SB, offset : u32, value : vec2<u32>)
+fn tint_symbol_4(buffer : SB, offset : u32, value : vec2<u32>)
 
 [[internal(intrinsic_store_vec2_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_5(buffer : [[access(read_write)]] SB, offset : u32, value : vec2<f32>)
+fn tint_symbol_5(buffer : SB, offset : u32, value : vec2<f32>)
 
 [[internal(intrinsic_store_vec3_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_6(buffer : [[access(read_write)]] SB, offset : u32, value : vec3<i32>)
+fn tint_symbol_6(buffer : SB, offset : u32, value : vec3<i32>)
 
 [[internal(intrinsic_store_vec3_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_7(buffer : [[access(read_write)]] SB, offset : u32, value : vec3<u32>)
+fn tint_symbol_7(buffer : SB, offset : u32, value : vec3<u32>)
 
 [[internal(intrinsic_store_vec3_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_8(buffer : [[access(read_write)]] SB, offset : u32, value : vec3<f32>)
+fn tint_symbol_8(buffer : SB, offset : u32, value : vec3<f32>)
 
 [[internal(intrinsic_store_vec4_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_9(buffer : [[access(read_write)]] SB, offset : u32, value : vec4<i32>)
+fn tint_symbol_9(buffer : SB, offset : u32, value : vec4<i32>)
 
 [[internal(intrinsic_store_vec4_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_10(buffer : [[access(read_write)]] SB, offset : u32, value : vec4<u32>)
+fn tint_symbol_10(buffer : SB, offset : u32, value : vec4<u32>)
 
 [[internal(intrinsic_store_vec4_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_11(buffer : [[access(read_write)]] SB, offset : u32, value : vec4<f32>)
+fn tint_symbol_11(buffer : SB, offset : u32, value : vec4<f32>)
 
-fn tint_symbol_12(buffer : [[access(read_write)]] SB, offset : u32, value : mat2x2<f32>) {
+fn tint_symbol_12(buffer : SB, offset : u32, value : mat2x2<f32>) {
   tint_symbol_5(buffer, (offset + 0u), value[0u]);
   tint_symbol_5(buffer, (offset + 8u), value[1u]);
 }
 
-fn tint_symbol_13(buffer : [[access(read_write)]] SB, offset : u32, value : mat2x3<f32>) {
+fn tint_symbol_13(buffer : SB, offset : u32, value : mat2x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_14(buffer : [[access(read_write)]] SB, offset : u32, value : mat2x4<f32>) {
+fn tint_symbol_14(buffer : SB, offset : u32, value : mat2x4<f32>) {
   tint_symbol_11(buffer, (offset + 0u), value[0u]);
   tint_symbol_11(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_15(buffer : [[access(read_write)]] SB, offset : u32, value : mat3x2<f32>) {
+fn tint_symbol_15(buffer : SB, offset : u32, value : mat3x2<f32>) {
   tint_symbol_5(buffer, (offset + 0u), value[0u]);
   tint_symbol_5(buffer, (offset + 8u), value[1u]);
   tint_symbol_5(buffer, (offset + 16u), value[2u]);
 }
 
-fn tint_symbol_16(buffer : [[access(read_write)]] SB, offset : u32, value : mat3x3<f32>) {
+fn tint_symbol_16(buffer : SB, offset : u32, value : mat3x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_17(buffer : [[access(read_write)]] SB, offset : u32, value : mat3x4<f32>) {
+fn tint_symbol_17(buffer : SB, offset : u32, value : mat3x4<f32>) {
   tint_symbol_11(buffer, (offset + 0u), value[0u]);
   tint_symbol_11(buffer, (offset + 16u), value[1u]);
   tint_symbol_11(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_18(buffer : [[access(read_write)]] SB, offset : u32, value : mat4x2<f32>) {
+fn tint_symbol_18(buffer : SB, offset : u32, value : mat4x2<f32>) {
   tint_symbol_5(buffer, (offset + 0u), value[0u]);
   tint_symbol_5(buffer, (offset + 8u), value[1u]);
   tint_symbol_5(buffer, (offset + 16u), value[2u]);
   tint_symbol_5(buffer, (offset + 24u), value[3u]);
 }
 
-fn tint_symbol_19(buffer : [[access(read_write)]] SB, offset : u32, value : mat4x3<f32>) {
+fn tint_symbol_19(buffer : SB, offset : u32, value : mat4x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
   tint_symbol_8(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_20(buffer : [[access(read_write)]] SB, offset : u32, value : mat4x4<f32>) {
+fn tint_symbol_20(buffer : SB, offset : u32, value : mat4x4<f32>) {
   tint_symbol_11(buffer, (offset + 0u), value[0u]);
   tint_symbol_11(buffer, (offset + 16u), value[1u]);
   tint_symbol_11(buffer, (offset + 32u), value[2u]);
   tint_symbol_11(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_21(buffer : [[access(read_write)]] SB, offset : u32, value : array<vec3<f32>, 2>) {
+fn tint_symbol_21(buffer : SB, offset : u32, value : array<vec3<f32>, 2>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
 }
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -457,7 +457,7 @@
   v : array<vec3<f32>, 2>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -493,86 +493,86 @@
 };
 
 [[internal(intrinsic_load_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32) -> i32
+fn tint_symbol(buffer : SB, offset : u32) -> i32
 
 [[internal(intrinsic_load_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_1(buffer : [[access(read_write)]] SB, offset : u32) -> u32
+fn tint_symbol_1(buffer : SB, offset : u32) -> u32
 
 [[internal(intrinsic_load_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_2(buffer : [[access(read_write)]] SB, offset : u32) -> f32
+fn tint_symbol_2(buffer : SB, offset : u32) -> f32
 
 [[internal(intrinsic_load_vec2_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_3(buffer : [[access(read_write)]] SB, offset : u32) -> vec2<i32>
+fn tint_symbol_3(buffer : SB, offset : u32) -> vec2<i32>
 
 [[internal(intrinsic_load_vec2_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_4(buffer : [[access(read_write)]] SB, offset : u32) -> vec2<u32>
+fn tint_symbol_4(buffer : SB, offset : u32) -> vec2<u32>
 
 [[internal(intrinsic_load_vec2_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_5(buffer : [[access(read_write)]] SB, offset : u32) -> vec2<f32>
+fn tint_symbol_5(buffer : SB, offset : u32) -> vec2<f32>
 
 [[internal(intrinsic_load_vec3_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_6(buffer : [[access(read_write)]] SB, offset : u32) -> vec3<i32>
+fn tint_symbol_6(buffer : SB, offset : u32) -> vec3<i32>
 
 [[internal(intrinsic_load_vec3_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_7(buffer : [[access(read_write)]] SB, offset : u32) -> vec3<u32>
+fn tint_symbol_7(buffer : SB, offset : u32) -> vec3<u32>
 
 [[internal(intrinsic_load_vec3_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_8(buffer : [[access(read_write)]] SB, offset : u32) -> vec3<f32>
+fn tint_symbol_8(buffer : SB, offset : u32) -> vec3<f32>
 
 [[internal(intrinsic_load_vec4_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_9(buffer : [[access(read_write)]] SB, offset : u32) -> vec4<i32>
+fn tint_symbol_9(buffer : SB, offset : u32) -> vec4<i32>
 
 [[internal(intrinsic_load_vec4_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_10(buffer : [[access(read_write)]] SB, offset : u32) -> vec4<u32>
+fn tint_symbol_10(buffer : SB, offset : u32) -> vec4<u32>
 
 [[internal(intrinsic_load_vec4_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_11(buffer : [[access(read_write)]] SB, offset : u32) -> vec4<f32>
+fn tint_symbol_11(buffer : SB, offset : u32) -> vec4<f32>
 
-fn tint_symbol_12(buffer : [[access(read_write)]] SB, offset : u32) -> mat2x2<f32> {
+fn tint_symbol_12(buffer : SB, offset : u32) -> mat2x2<f32> {
   return mat2x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_13(buffer : [[access(read_write)]] SB, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_13(buffer : SB, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_14(buffer : [[access(read_write)]] SB, offset : u32) -> mat2x4<f32> {
+fn tint_symbol_14(buffer : SB, offset : u32) -> mat2x4<f32> {
   return mat2x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_15(buffer : [[access(read_write)]] SB, offset : u32) -> mat3x2<f32> {
+fn tint_symbol_15(buffer : SB, offset : u32) -> mat3x2<f32> {
   return mat3x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_16(buffer : [[access(read_write)]] SB, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_16(buffer : SB, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_17(buffer : [[access(read_write)]] SB, offset : u32) -> mat3x4<f32> {
+fn tint_symbol_17(buffer : SB, offset : u32) -> mat3x4<f32> {
   return mat3x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_18(buffer : [[access(read_write)]] SB, offset : u32) -> mat4x2<f32> {
+fn tint_symbol_18(buffer : SB, offset : u32) -> mat4x2<f32> {
   return mat4x2<f32>(tint_symbol_5(buffer, (offset + 0u)), tint_symbol_5(buffer, (offset + 8u)), tint_symbol_5(buffer, (offset + 16u)), tint_symbol_5(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_19(buffer : [[access(read_write)]] SB, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_19(buffer : SB, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_20(buffer : [[access(read_write)]] SB, offset : u32) -> mat4x4<f32> {
+fn tint_symbol_20(buffer : SB, offset : u32) -> mat4x4<f32> {
   return mat4x4<f32>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 32u)), tint_symbol_11(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_21(buffer : [[access(read_write)]] SB, offset : u32) -> array<vec3<f32>, 2> {
+fn tint_symbol_21(buffer : SB, offset : u32) -> array<vec3<f32>, 2> {
   return array<vec3<f32>, 2>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_22(buffer : [[access(read_write)]] SB, offset : u32) -> SB {
+fn tint_symbol_22(buffer : SB, offset : u32) -> SB {
   return SB(tint_symbol(buffer, (offset + 0u)), tint_symbol_1(buffer, (offset + 4u)), tint_symbol_2(buffer, (offset + 8u)), tint_symbol_3(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)), tint_symbol_5(buffer, (offset + 32u)), tint_symbol_6(buffer, (offset + 48u)), tint_symbol_7(buffer, (offset + 64u)), tint_symbol_8(buffer, (offset + 80u)), tint_symbol_9(buffer, (offset + 96u)), tint_symbol_10(buffer, (offset + 112u)), tint_symbol_11(buffer, (offset + 128u)), tint_symbol_12(buffer, (offset + 144u)), tint_symbol_13(buffer, (offset + 160u)), tint_symbol_14(buffer, (offset + 192u)), tint_symbol_15(buffer, (offset + 224u)), tint_symbol_16(buffer, (offset + 256u)), tint_symbol_17(buffer, (offset + 304u)), tint_symbol_18(buffer, (offset + 352u)), tint_symbol_19(buffer, (offset + 384u)), tint_symbol_20(buffer, (offset + 448u)), tint_symbol_21(buffer, (offset + 512u)));
 }
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -613,7 +613,7 @@
   v : array<vec3<f32>, 2>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -649,101 +649,101 @@
 };
 
 [[internal(intrinsic_store_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32, value : i32)
+fn tint_symbol(buffer : SB, offset : u32, value : i32)
 
 [[internal(intrinsic_store_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_1(buffer : [[access(read_write)]] SB, offset : u32, value : u32)
+fn tint_symbol_1(buffer : SB, offset : u32, value : u32)
 
 [[internal(intrinsic_store_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_2(buffer : [[access(read_write)]] SB, offset : u32, value : f32)
+fn tint_symbol_2(buffer : SB, offset : u32, value : f32)
 
 [[internal(intrinsic_store_vec2_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_3(buffer : [[access(read_write)]] SB, offset : u32, value : vec2<i32>)
+fn tint_symbol_3(buffer : SB, offset : u32, value : vec2<i32>)
 
 [[internal(intrinsic_store_vec2_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_4(buffer : [[access(read_write)]] SB, offset : u32, value : vec2<u32>)
+fn tint_symbol_4(buffer : SB, offset : u32, value : vec2<u32>)
 
 [[internal(intrinsic_store_vec2_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_5(buffer : [[access(read_write)]] SB, offset : u32, value : vec2<f32>)
+fn tint_symbol_5(buffer : SB, offset : u32, value : vec2<f32>)
 
 [[internal(intrinsic_store_vec3_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_6(buffer : [[access(read_write)]] SB, offset : u32, value : vec3<i32>)
+fn tint_symbol_6(buffer : SB, offset : u32, value : vec3<i32>)
 
 [[internal(intrinsic_store_vec3_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_7(buffer : [[access(read_write)]] SB, offset : u32, value : vec3<u32>)
+fn tint_symbol_7(buffer : SB, offset : u32, value : vec3<u32>)
 
 [[internal(intrinsic_store_vec3_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_8(buffer : [[access(read_write)]] SB, offset : u32, value : vec3<f32>)
+fn tint_symbol_8(buffer : SB, offset : u32, value : vec3<f32>)
 
 [[internal(intrinsic_store_vec4_u32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_9(buffer : [[access(read_write)]] SB, offset : u32, value : vec4<i32>)
+fn tint_symbol_9(buffer : SB, offset : u32, value : vec4<i32>)
 
 [[internal(intrinsic_store_vec4_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_10(buffer : [[access(read_write)]] SB, offset : u32, value : vec4<u32>)
+fn tint_symbol_10(buffer : SB, offset : u32, value : vec4<u32>)
 
 [[internal(intrinsic_store_vec4_i32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol_11(buffer : [[access(read_write)]] SB, offset : u32, value : vec4<f32>)
+fn tint_symbol_11(buffer : SB, offset : u32, value : vec4<f32>)
 
-fn tint_symbol_12(buffer : [[access(read_write)]] SB, offset : u32, value : mat2x2<f32>) {
+fn tint_symbol_12(buffer : SB, offset : u32, value : mat2x2<f32>) {
   tint_symbol_5(buffer, (offset + 0u), value[0u]);
   tint_symbol_5(buffer, (offset + 8u), value[1u]);
 }
 
-fn tint_symbol_13(buffer : [[access(read_write)]] SB, offset : u32, value : mat2x3<f32>) {
+fn tint_symbol_13(buffer : SB, offset : u32, value : mat2x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_14(buffer : [[access(read_write)]] SB, offset : u32, value : mat2x4<f32>) {
+fn tint_symbol_14(buffer : SB, offset : u32, value : mat2x4<f32>) {
   tint_symbol_11(buffer, (offset + 0u), value[0u]);
   tint_symbol_11(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_15(buffer : [[access(read_write)]] SB, offset : u32, value : mat3x2<f32>) {
+fn tint_symbol_15(buffer : SB, offset : u32, value : mat3x2<f32>) {
   tint_symbol_5(buffer, (offset + 0u), value[0u]);
   tint_symbol_5(buffer, (offset + 8u), value[1u]);
   tint_symbol_5(buffer, (offset + 16u), value[2u]);
 }
 
-fn tint_symbol_16(buffer : [[access(read_write)]] SB, offset : u32, value : mat3x3<f32>) {
+fn tint_symbol_16(buffer : SB, offset : u32, value : mat3x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_17(buffer : [[access(read_write)]] SB, offset : u32, value : mat3x4<f32>) {
+fn tint_symbol_17(buffer : SB, offset : u32, value : mat3x4<f32>) {
   tint_symbol_11(buffer, (offset + 0u), value[0u]);
   tint_symbol_11(buffer, (offset + 16u), value[1u]);
   tint_symbol_11(buffer, (offset + 32u), value[2u]);
 }
 
-fn tint_symbol_18(buffer : [[access(read_write)]] SB, offset : u32, value : mat4x2<f32>) {
+fn tint_symbol_18(buffer : SB, offset : u32, value : mat4x2<f32>) {
   tint_symbol_5(buffer, (offset + 0u), value[0u]);
   tint_symbol_5(buffer, (offset + 8u), value[1u]);
   tint_symbol_5(buffer, (offset + 16u), value[2u]);
   tint_symbol_5(buffer, (offset + 24u), value[3u]);
 }
 
-fn tint_symbol_19(buffer : [[access(read_write)]] SB, offset : u32, value : mat4x3<f32>) {
+fn tint_symbol_19(buffer : SB, offset : u32, value : mat4x3<f32>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
   tint_symbol_8(buffer, (offset + 32u), value[2u]);
   tint_symbol_8(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_20(buffer : [[access(read_write)]] SB, offset : u32, value : mat4x4<f32>) {
+fn tint_symbol_20(buffer : SB, offset : u32, value : mat4x4<f32>) {
   tint_symbol_11(buffer, (offset + 0u), value[0u]);
   tint_symbol_11(buffer, (offset + 16u), value[1u]);
   tint_symbol_11(buffer, (offset + 32u), value[2u]);
   tint_symbol_11(buffer, (offset + 48u), value[3u]);
 }
 
-fn tint_symbol_21(buffer : [[access(read_write)]] SB, offset : u32, value : array<vec3<f32>, 2>) {
+fn tint_symbol_21(buffer : SB, offset : u32, value : array<vec3<f32>, 2>) {
   tint_symbol_8(buffer, (offset + 0u), value[0u]);
   tint_symbol_8(buffer, (offset + 16u), value[1u]);
 }
 
-fn tint_symbol_22(buffer : [[access(read_write)]] SB, offset : u32, value : SB) {
+fn tint_symbol_22(buffer : SB, offset : u32, value : SB) {
   tint_symbol(buffer, (offset + 0u), value.a);
   tint_symbol_1(buffer, (offset + 4u), value.b);
   tint_symbol_2(buffer, (offset + 8u), value.c);
@@ -768,7 +768,7 @@
   tint_symbol_21(buffer, (offset + 512u), value.v);
 }
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -802,7 +802,7 @@
   b : [[stride(256)]] array<S2>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -838,9 +838,9 @@
 };
 
 [[internal(intrinsic_load_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32) -> f32
+fn tint_symbol(buffer : SB, offset : u32) -> f32
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -874,7 +874,7 @@
   b : [[stride(256)]] array<S2>;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -906,9 +906,9 @@
 };
 
 [[internal(intrinsic_load_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32) -> f32
+fn tint_symbol(buffer : SB, offset : u32) -> f32
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -953,7 +953,7 @@
   b : A2_Array;
 };
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
@@ -993,9 +993,9 @@
 };
 
 [[internal(intrinsic_load_f32), internal(disable_validation__function_has_no_body)]]
-fn tint_symbol(buffer : [[access(read_write)]] SB, offset : u32) -> f32
+fn tint_symbol(buffer : SB, offset : u32) -> f32
 
-[[group(0), binding(0)]] var<storage> sb : [[access(read_write)]] SB;
+[[group(0), binding(0)]] var<storage, read_write> sb : SB;
 
 [[stage(compute)]]
 fn main() {
diff --git a/src/transform/external_texture_transform.cc b/src/transform/external_texture_transform.cc
index 036c296..81e4fab 100644
--- a/src/transform/external_texture_transform.cc
+++ b/src/transform/external_texture_transform.cc
@@ -115,8 +115,9 @@
         auto* clonedConstructor = ctx.Clone(var->constructor());
         auto clonedDecorations = ctx.Clone(var->decorations());
         auto* newVar = ctx.dst->create<ast::Variable>(
-            clonedSrc, clonedSym, var->declared_storage_class(), newType,
-            var->is_const(), clonedConstructor, clonedDecorations);
+            clonedSrc, clonedSym, var->declared_storage_class(),
+            var->declared_access(), newType, var->is_const(), clonedConstructor,
+            clonedDecorations);
 
         ctx.Replace(var, newVar);
       }
diff --git a/src/transform/vertex_pulling.cc b/src/transform/vertex_pulling.cc
index 296e2d8..9f3b3bf 100644
--- a/src/transform/vertex_pulling.cc
+++ b/src/transform/vertex_pulling.cc
@@ -214,10 +214,10 @@
             ctx.dst->create<ast::StructBlockDecoration>(),
         });
     for (uint32_t i = 0; i < cfg.vertex_state.size(); ++i) {
-      auto* access = ctx.dst->ty.access(ast::AccessControl::kRead, struct_type);
       // The decorated variable with struct type
       ctx.dst->Global(
-          GetVertexBufferName(i), access, ast::StorageClass::kStorage, nullptr,
+          GetVertexBufferName(i), struct_type, ast::StorageClass::kStorage,
+          ast::Access::kRead,
           ast::DecorationList{
               ctx.dst->create<ast::BindingDecoration>(i),
               ctx.dst->create<ast::GroupDecoration>(cfg.pulling_group),
diff --git a/src/transform/vertex_pulling_test.cc b/src/transform/vertex_pulling_test.cc
index 1e6ff9e..04e5f6a 100644
--- a/src/transform/vertex_pulling_test.cc
+++ b/src/transform/vertex_pulling_test.cc
@@ -121,7 +121,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4<f32> {
@@ -161,7 +161,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(instance_index)]] tint_pulling_instance_index : u32) -> [[builtin(position)]] vec4<f32> {
@@ -201,7 +201,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(5)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(5)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4<f32> {
@@ -246,7 +246,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 struct Inputs {
   [[location(0)]]
@@ -296,9 +296,9 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
-[[binding(1), group(4)]] var<storage> tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData;
+[[binding(1), group(4)]] var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(vertex_index)]] custom_vertex_index : u32, [[builtin(instance_index)]] custom_instance_index : u32) -> [[builtin(position)]] vec4<f32> {
@@ -358,9 +358,9 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
-[[binding(1), group(4)]] var<storage> tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData;
+[[binding(1), group(4)]] var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
 
 struct tint_symbol {
   [[builtin(vertex_index)]]
@@ -442,9 +442,9 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
-[[binding(1), group(4)]] var<storage> tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData;
+[[binding(1), group(4)]] var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
 
 struct Inputs {
   [[location(0)]]
@@ -511,7 +511,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4<f32> {
@@ -559,11 +559,11 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
-[[binding(1), group(4)]] var<storage> tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData;
+[[binding(1), group(4)]] var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
 
-[[binding(2), group(4)]] var<storage> tint_pulling_vertex_buffer_2 : [[access(read)]] TintVertexData;
+[[binding(2), group(4)]] var<storage, read> tint_pulling_vertex_buffer_2 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(vertex_index)]] tint_pulling_vertex_index : u32) -> [[builtin(position)]] vec4<f32> {
@@ -617,7 +617,7 @@
   tint_vertex_data_1 : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0_1 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0_1 : TintVertexData;
 
 [[stage(vertex)]]
 fn main([[builtin(vertex_index)]] tint_pulling_vertex_index_1 : u32) -> [[builtin(position)]] vec4<f32> {
@@ -671,7 +671,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(5)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(5)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 var<private> var_a : f32;
 
@@ -720,9 +720,9 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
-[[binding(1), group(4)]] var<storage> tint_pulling_vertex_buffer_1 : [[access(read)]] TintVertexData;
+[[binding(1), group(4)]] var<storage, read> tint_pulling_vertex_buffer_1 : TintVertexData;
 
 var<private> var_a : f32;
 
@@ -787,7 +787,7 @@
   tint_vertex_data : [[stride(4)]] array<u32>;
 };
 
-[[binding(0), group(4)]] var<storage> tint_pulling_vertex_buffer_0 : [[access(read)]] TintVertexData;
+[[binding(0), group(4)]] var<storage, read> tint_pulling_vertex_buffer_0 : TintVertexData;
 
 var<private> var_a : f32;
 
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index d25dc88..394017d 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -248,8 +248,8 @@
   }
 
   out << "as";
-  if (!EmitType(out, type, ast::StorageClass::kNone,
-                ast::AccessControl::kReadWrite, "")) {
+  if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite,
+                "")) {
     return false;
   }
   out << "(";
@@ -1303,8 +1303,8 @@
   if (brackets) {
     out << "{";
   } else {
-    if (!EmitType(out, type, ast::StorageClass::kNone,
-                  ast::AccessControl::kReadWrite, "")) {
+    if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite,
+                  "")) {
       return false;
     }
     out << "(";
@@ -1566,7 +1566,7 @@
   auto* func = builder_.Sem().Get(func_ast);
 
   if (!EmitType(out, func->ReturnType(), ast::StorageClass::kNone,
-                ast::AccessControl::kReadWrite, "")) {
+                ast::Access::kReadWrite, "")) {
     return false;
   }
 
@@ -1633,7 +1633,7 @@
     // buffers. These functions have a storage buffer parameter with
     // StorageClass::kStorage. This is required to correctly translate the
     // parameter to [RW]ByteAddressBuffer.
-    if (!EmitType(out, type, v->StorageClass(), v->AccessControl(),
+    if (!EmitType(out, type, v->StorageClass(), v->Access(),
                   builder_.Symbols().NameFor(v->Declaration()->symbol()))) {
       return false;
     }
@@ -1720,7 +1720,7 @@
 
       increment_indent();
       make_indent(out);
-      if (!EmitType(out, type, var->StorageClass(), var->AccessControl(), "")) {
+      if (!EmitType(out, type, var->StorageClass(), var->Access(), "")) {
         return false;
       }
       out << " " << builder_.Symbols().NameFor(decl->symbol()) << ";"
@@ -1746,15 +1746,13 @@
     }
 
     auto* type = var->Type()->UnwrapRef();
-    if (!EmitType(out, type, ast::StorageClass::kStorage, var->AccessControl(),
-                  "")) {
+    if (!EmitType(out, type, ast::StorageClass::kStorage, var->Access(), "")) {
       return false;
     }
 
     out << " " << builder_.Symbols().NameFor(decl->symbol())
-        << RegisterAndSpace(
-               var->AccessControl() == ast::AccessControl::kRead ? 't' : 'u',
-               binding_point)
+        << RegisterAndSpace(var->Access() == ast::Access::kRead ? 't' : 'u',
+                            binding_point)
         << ";" << std::endl;
     emitted_storagebuffer = true;
   }
@@ -1781,7 +1779,7 @@
       auto* type = sem->Type()->UnwrapRef();
 
       make_indent(out);
-      if (!EmitType(out, type, sem->StorageClass(), sem->AccessControl(),
+      if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
                     builder_.Symbols().NameFor(var->symbol()))) {
         return false;
       }
@@ -1832,7 +1830,7 @@
       auto* type = sem->Type()->UnwrapRef();
 
       make_indent(out);
-      if (!EmitType(out, type, sem->StorageClass(), sem->AccessControl(),
+      if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
                     builder_.Symbols().NameFor(var->symbol()))) {
         return false;
       }
@@ -1903,8 +1901,7 @@
 
       auto name = builder_.Symbols().NameFor(decl->symbol());
       auto* type = var->Type()->UnwrapRef();
-      if (!EmitType(out, type, var->StorageClass(), var->AccessControl(),
-                    name)) {
+      if (!EmitType(out, type, var->StorageClass(), var->Access(), name)) {
         return false;
       }
       if (!type->Is<sem::Array>()) {
@@ -1916,7 +1913,7 @@
       if (unwrapped_type->Is<sem::Texture>()) {
         register_space = "t";
         if (auto* storage_tex = unwrapped_type->As<sem::StorageTexture>()) {
-          if (storage_tex->access_control() != ast::AccessControl::kRead) {
+          if (storage_tex->access() != ast::Access::kRead) {
             register_space = "u";
           }
         }
@@ -2043,7 +2040,7 @@
     }
     first = false;
 
-    if (!EmitType(out, type, sem->StorageClass(), sem->AccessControl(), "")) {
+    if (!EmitType(out, type, sem->StorageClass(), sem->Access(), "")) {
       return false;
     }
 
@@ -2111,8 +2108,8 @@
   } else if (type->Is<sem::U32>()) {
     out << "0u";
   } else if (auto* vec = type->As<sem::Vector>()) {
-    if (!EmitType(out, type, ast::StorageClass::kNone,
-                  ast::AccessControl::kReadWrite, "")) {
+    if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite,
+                  "")) {
       return false;
     }
     ScopedParen sp(out);
@@ -2125,8 +2122,8 @@
       }
     }
   } else if (auto* mat = type->As<sem::Matrix>()) {
-    if (!EmitType(out, type, ast::StorageClass::kNone,
-                  ast::AccessControl::kReadWrite, "")) {
+    if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite,
+                  "")) {
       return false;
     }
     ScopedParen sp(out);
@@ -2326,10 +2323,10 @@
 bool GeneratorImpl::EmitType(std::ostream& out,
                              const sem::Type* type,
                              ast::StorageClass storage_class,
-                             ast::AccessControl::Access access_control,
+                             ast::Access access,
                              const std::string& name) {
   if (storage_class == ast::StorageClass::kStorage) {
-    if (access_control != ast::AccessControl::kRead) {
+    if (access != ast::Access::kRead) {
       out << "RW";
     }
     out << "ByteAddressBuffer";
@@ -2349,7 +2346,7 @@
       sizes.push_back(arr->Count());
       base_type = arr->ElemType();
     }
-    if (!EmitType(out, base_type, storage_class, access_control, "")) {
+    if (!EmitType(out, base_type, storage_class, access, "")) {
       return false;
     }
     if (!name.empty()) {
@@ -2365,7 +2362,7 @@
   } else if (type->Is<sem::I32>()) {
     out << "int";
   } else if (auto* mat = type->As<sem::Matrix>()) {
-    if (!EmitType(out, mat->type(), storage_class, access_control, "")) {
+    if (!EmitType(out, mat->type(), storage_class, access, "")) {
       return false;
     }
     // Note: HLSL's matrices are declared as <type>NxM, where N is the number of
@@ -2394,10 +2391,8 @@
     auto* multism = tex->As<sem::MultisampledTexture>();
     auto* sampled = tex->As<sem::SampledTexture>();
 
-    if (storage) {
-      if (access_control != ast::AccessControl::kRead) {
-        out << "RW";
-      }
+    if (storage && storage->access() != ast::Access::kRead) {
+      out << "RW";
     }
     out << "Texture";
 
@@ -2461,7 +2456,7 @@
       out << "uint" << size;
     } else {
       out << "vector<";
-      if (!EmitType(out, vec->type(), storage_class, access_control, "")) {
+      if (!EmitType(out, vec->type(), storage_class, access, "")) {
         return false;
       }
       out << ", " << size << ">";
@@ -2498,7 +2493,7 @@
 
     auto mem_name = builder_.Symbols().NameFor(mem->Declaration()->symbol());
     if (!EmitType(out, mem->Type(), ast::StorageClass::kNone,
-                  ast::AccessControl::kReadWrite, mem_name)) {
+                  ast::Access::kReadWrite, mem_name)) {
       return false;
     }
     // Array member name will be output with the type
@@ -2602,7 +2597,7 @@
   if (var->is_const()) {
     out << "const ";
   }
-  if (!EmitType(out, type, sem->StorageClass(), sem->AccessControl(),
+  if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
                 builder_.Symbols().NameFor(var->symbol()))) {
     return false;
   }
@@ -2655,7 +2650,7 @@
     }
     out << "#endif" << std::endl;
     out << "static const ";
-    if (!EmitType(out, type, sem->StorageClass(), sem->AccessControl(),
+    if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
                   builder_.Symbols().NameFor(var->symbol()))) {
       return false;
     }
@@ -2663,7 +2658,7 @@
         << kSpecConstantPrefix << const_id << ";" << std::endl;
   } else {
     out << "static const ";
-    if (!EmitType(out, type, sem->StorageClass(), sem->AccessControl(),
+    if (!EmitType(out, type, sem->StorageClass(), sem->Access(),
                   builder_.Symbols().NameFor(var->symbol()))) {
       return false;
     }
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index 9fee784..1b62c76 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -38,9 +38,6 @@
 
 // Forward declarations
 namespace sem {
-class AccessControl;
-}  // namespace sem
-namespace sem {
 class Call;
 class Intrinsic;
 }  // namespace sem
@@ -288,13 +285,13 @@
   /// @param out the output stream
   /// @param type the type to generate
   /// @param storage_class the storage class of the variable
-  /// @param access_control the access control type of the variable
+  /// @param access the access control type of the variable
   /// @param name the name of the variable, only used for array emission
   /// @returns true if the type is emitted
   bool EmitType(std::ostream& out,
                 const sem::Type* type,
                 ast::StorageClass storage_class,
-                ast::AccessControl::Access access_control,
+                ast::Access access,
                 const std::string& name);
   /// Handles generating a structure declaration
   /// @param out the output stream
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index b828607..e3b3d37 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -428,9 +428,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -474,9 +472,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -520,9 +516,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kWrite, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -563,9 +557,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -814,8 +806,7 @@
        Emit_Decoration_Called_By_EntryPoint_With_StorageBuffer) {
   auto* s = Structure("S", {Member("x", ty.f32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -1064,9 +1055,7 @@
   auto* s = Structure("Data", {Member("d", ty.f32())},
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("data", ac, ast::StorageClass::kStorage,
+  Global("data", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc
index e155281..db9d468 100644
--- a/src/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -98,9 +98,7 @@
     auto* s =
         b.Structure("Data", members, {b.create<ast::StructBlockDecoration>()});
 
-    auto* ac_ty = b.ty.access(ast::AccessControl::kReadWrite, s);
-
-    b.Global("data", ac_ty, ast::StorageClass::kStorage, nullptr,
+    b.Global("data", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
              ast::DecorationList{
                  b.create<ast::BindingDecoration>(0),
                  b.create<ast::GroupDecoration>(1),
diff --git a/src/writer/hlsl/generator_impl_sanitizer_test.cc b/src/writer/hlsl/generator_impl_sanitizer_test.cc
index 7e16d5b..25a584a 100644
--- a/src/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -33,9 +33,8 @@
                           {
                               create<ast::StructBlockDecoration>(),
                           });
-  auto* ac_ty = ty.access(ast::AccessControl::kRead, sb_ty);
 
-  Global("sb", ac_ty, ast::StorageClass::kStorage,
+  Global("sb", sb_ty, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc
index 090e077..94dd0d6 100644
--- a/src/writer/hlsl/generator_impl_type_test.cc
+++ b/src/writer/hlsl/generator_impl_type_test.cc
@@ -38,7 +38,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, "ary"))
+                           ast::Access::kReadWrite, "ary"))
       << gen.error();
   EXPECT_EQ(result(), "bool ary[4]");
 }
@@ -50,7 +50,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, "ary"))
+                           ast::Access::kReadWrite, "ary"))
       << gen.error();
   EXPECT_EQ(result(), "bool ary[5][4]");
 }
@@ -64,7 +64,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, "ary"))
+                           ast::Access::kReadWrite, "ary"))
       << gen.error();
   EXPECT_EQ(result(), "bool ary[5][4][1]");
 }
@@ -76,7 +76,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, "ary"))
+                           ast::Access::kReadWrite, "ary"))
       << gen.error();
   EXPECT_EQ(result(), "bool ary[6][5][4]");
 }
@@ -88,7 +88,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "bool[4]");
 }
@@ -100,7 +100,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, "ary"))
+                           ast::Access::kReadWrite, "ary"))
       << gen.error();
   EXPECT_EQ(result(), "bool ary[]");
 }
@@ -111,7 +111,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, bool_, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "bool");
 }
@@ -122,7 +122,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, f32, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "float");
 }
@@ -133,7 +133,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, i32, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "int");
 }
@@ -146,7 +146,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "float2x3");
 }
@@ -159,7 +159,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, p, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "float*");
 }
@@ -189,8 +189,7 @@
                           Member("b", ty.f32()),
                       },
                       {create<ast::StructBlockDecoration>()});
-  Global("g", ty.access(ast::AccessControl::kReadWrite, s),
-         ast::StorageClass::kStorage,
+  Global("g", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -214,7 +213,7 @@
 
   auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
   ASSERT_TRUE(gen.EmitType(out, sem_s, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "S");
 }
@@ -234,7 +233,7 @@
 
   auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
   ASSERT_TRUE(gen.EmitType(out, sem_s, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(gen.result(), R"(struct S {
   int a;
@@ -290,7 +289,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, u32, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "uint");
 }
@@ -302,7 +301,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, vec3, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "float3");
 }
@@ -313,7 +312,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, void_, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "void");
 }
@@ -324,7 +323,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, sampler, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "SamplerState");
 }
@@ -335,7 +334,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, sampler, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "SamplerComparisonState");
 }
@@ -354,7 +353,7 @@
 
   auto* t = ty.depth_texture(params.dim);
 
-  Global("tex", t, ast::StorageClass::kNone, nullptr,
+  Global("tex", t,
          ast::DecorationList{
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
@@ -413,7 +412,7 @@
   }
   auto* t = ty.sampled_texture(params.dim, datatype);
 
-  Global("tex", t, ast::StorageClass::kNone, nullptr,
+  Global("tex", t,
          ast::DecorationList{
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
@@ -532,7 +531,7 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitType(out, s, ast::StorageClass::kNone,
-                           ast::AccessControl::kReadWrite, ""))
+                           ast::Access::kReadWrite, ""))
       << gen.error();
   EXPECT_EQ(result(), "Texture2DMS<float4>");
 }
@@ -552,11 +551,11 @@
 TEST_P(HlslStorageTexturesTest, Emit) {
   auto params = GetParam();
 
-  auto* t = ty.storage_texture(params.dim, params.imgfmt);
-  auto* ac = ty.access(
-      params.ro ? ast::AccessControl::kRead : ast::AccessControl::kWrite, t);
+  auto* t =
+      ty.storage_texture(params.dim, params.imgfmt,
+                         params.ro ? ast::Access::kRead : ast::Access::kWrite);
 
-  Global("tex", ac, ast::StorageClass::kNone, nullptr,
+  Global("tex", t,
          ast::DecorationList{
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 6eec4df..f4d38fc 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -1326,7 +1326,7 @@
     }
     first = false;
 
-    if (var->AccessControl() == ast::AccessControl::kRead) {
+    if (var->Access() == ast::Access::kRead) {
       out_ << "const ";
     }
 
@@ -1544,7 +1544,7 @@
     auto* binding = data.second.binding;
     // auto* set = data.second.set;
 
-    if (var->AccessControl() == ast::AccessControl::kRead) {
+    if (var->Access() == ast::Access::kRead) {
       out_ << "const ";
     }
 
@@ -2037,9 +2037,9 @@
       }
 
       std::string access_str;
-      if (storage->access_control() == ast::AccessControl::kRead) {
+      if (storage->access() == ast::Access::kRead) {
         out_ << ", access::read";
-      } else if (storage->access_control() == ast::AccessControl::kWrite) {
+      } else if (storage->access() == ast::Access::kWrite) {
         out_ << ", access::write";
       } else {
         diagnostics_.add_error("Invalid access control for storage texture");
diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc
index 7c19271..66df0a3 100644
--- a/src/writer/msl/generator_impl_function_test.cc
+++ b/src/writer/msl/generator_impl_function_test.cc
@@ -306,9 +306,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -354,9 +352,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -620,9 +616,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -679,9 +673,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-
-  Global("coord", ac, ast::StorageClass::kStorage,
+  Global("coord", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(1),
@@ -824,9 +816,7 @@
   auto* s = Structure("Data", {Member("d", ty.f32())},
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("data", ac, ast::StorageClass::kStorage,
+  Global("data", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc
index 235fc26..d45ead6 100644
--- a/src/writer/msl/generator_impl_type_test.cc
+++ b/src/writer/msl/generator_impl_type_test.cc
@@ -232,8 +232,7 @@
                 },
                 {create<ast::StructBlockDecoration>()});
 
-  Global("G", ty.access(ast::AccessControl::kRead, s),
-         ast::StorageClass::kStorage,
+  Global("G", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -342,8 +341,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  Global("G", ty.access(ast::AccessControl::kRead, s),
-         ast::StorageClass::kStorage,
+  Global("G", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -437,8 +435,7 @@
                 },
                 ast::DecorationList{create<ast::StructBlockDecoration>()});
 
-  Global("G", ty.access(ast::AccessControl::kRead, s),
-         ast::StorageClass::kStorage,
+  Global("G", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -551,8 +548,7 @@
       },
       {create<ast::StructBlockDecoration>()});
 
-  Global("G", ty.access(ast::AccessControl::kRead, s),
-         ast::StorageClass::kStorage,
+  Global("G", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -617,8 +613,7 @@
                       },
                       {create<ast::StructBlockDecoration>()});
 
-  Global("G", ty.access(ast::AccessControl::kRead, s),
-         ast::StorageClass::kStorage,
+  Global("G", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -771,10 +766,10 @@
 TEST_P(MslStorageTexturesTest, Emit) {
   auto params = GetParam();
 
-  auto* s = ty.storage_texture(params.dim, ast::ImageFormat::kR32Float);
-  auto* ac = ty.access(
-      params.ro ? ast::AccessControl::kRead : ast::AccessControl::kWrite, s);
-  Global("test_var", ac,
+  auto* s =
+      ty.storage_texture(params.dim, ast::ImageFormat::kR32Float,
+                         params.ro ? ast::Access::kRead : ast::Access::kWrite);
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -782,7 +777,7 @@
 
   GeneratorImpl& gen = Build();
 
-  ASSERT_TRUE(gen.EmitType(program->TypeOf(ac), "")) << gen.error();
+  ASSERT_TRUE(gen.EmitType(program->TypeOf(s), "")) << gen.error();
   EXPECT_EQ(gen.result(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index a076318..6ebafb9 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -786,20 +786,23 @@
   if (var->has_constructor()) {
     ops.push_back(Operand::Int(init_id));
   } else {
-    if (type->Is<sem::StorageTexture>() || type->Is<sem::Struct>()) {
+    auto* st = type->As<sem::StorageTexture>();
+    if (st || type->Is<sem::Struct>()) {
       // type is a sem::Struct or a sem::StorageTexture
-      switch (sem->AccessControl()) {
-        case ast::AccessControl::kWrite:
+      auto access = st ? st->access() : sem->Access();
+      switch (access) {
+        case ast::Access::kWrite:
           push_annot(
               spv::Op::OpDecorate,
               {Operand::Int(var_id), Operand::Int(SpvDecorationNonReadable)});
           break;
-        case ast::AccessControl::kRead:
+        case ast::Access::kRead:
           push_annot(
               spv::Op::OpDecorate,
               {Operand::Int(var_id), Operand::Int(SpvDecorationNonWritable)});
           break;
-        case ast::AccessControl::kReadWrite:
+        case ast::Access::kUndefined:
+        case ast::Access::kReadWrite:
           break;
       }
     }
@@ -3287,17 +3290,17 @@
         type_name_to_id_[builder_
                              .create<sem::StorageTexture>(
                                  st->dim(), st->image_format(),
-                                 ast::AccessControl::kRead, st->type())
+                                 ast::Access::kRead, st->type())
                              ->type_name()] = id;
         type_name_to_id_[builder_
                              .create<sem::StorageTexture>(
                                  st->dim(), st->image_format(),
-                                 ast::AccessControl::kWrite, st->type())
+                                 ast::Access::kWrite, st->type())
                              ->type_name()] = id;
         type_name_to_id_[builder_
                              .create<sem::StorageTexture>(
                                  st->dim(), st->image_format(),
-                                 ast::AccessControl::kReadWrite, st->type())
+                                 ast::Access::kReadWrite, st->type())
                              ->type_name()] = id;
       }
 
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 6cdd12a..aa051c4 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -263,9 +263,9 @@
   /// @param type the type to generate for
   /// @param struct_id the struct id
   /// @param member_idx the member index
-  void GenerateMemberAccessControlIfNeeded(const sem::Type* type,
-                                           uint32_t struct_id,
-                                           uint32_t member_idx);
+  void GenerateMemberAccessIfNeeded(const sem::Type* type,
+                                    uint32_t struct_id,
+                                    uint32_t member_idx);
   /// Generates a function variable
   /// @param var the variable
   /// @returns true if the variable was generated
diff --git a/src/writer/spirv/builder_function_test.cc b/src/writer/spirv/builder_function_test.cc
index a3efcf7..d4ed7fb 100644
--- a/src/writer/spirv/builder_function_test.cc
+++ b/src/writer/spirv/builder_function_test.cc
@@ -204,9 +204,7 @@
   auto* s = Structure("Data", {Member("d", ty.f32())},
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("data", ac, ast::StorageClass::kStorage, nullptr,
+  Global("data", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index 622e7df..6cc49f9d 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -394,7 +394,7 @@
   // struct A {
   //   a : i32;
   // };
-  // var b : [[access(read)]] A
+  // var b<storage, read> : A
 
   auto* A = Structure("A",
                       {
@@ -402,9 +402,8 @@
                           Member("b", ty.i32()),
                       },
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, A);
 
-  auto* var = Global("b", ac, ast::StorageClass::kStorage,
+  auto* var = Global("b", A, ast::StorageClass::kStorage, ast::Access::kRead,
                      ast::DecorationList{
                          create<ast::BindingDecoration>(0),
                          create<ast::GroupDecoration>(0),
@@ -438,14 +437,13 @@
   //   a : i32;
   // };
   // type B = A;
-  // var b : [[access(read)]] B
+  // var b<storage, read> : B
 
   auto* A = Structure("A", {Member("a", ty.i32())},
                       {create<ast::StructBlockDecoration>()});
   auto* B = ty.alias("B", A);
   AST().AddConstructedType(B);
-  auto* ac = ty.access(ast::AccessControl::kRead, B);
-  auto* var = Global("b", ac, ast::StorageClass::kStorage,
+  auto* var = Global("b", B, ast::StorageClass::kStorage, ast::Access::kRead,
                      ast::DecorationList{
                          create<ast::BindingDecoration>(0),
                          create<ast::GroupDecoration>(0),
@@ -476,15 +474,14 @@
   // struct A {
   //   a : i32;
   // };
-  // type B = [[access(read)]] A;
-  // var b : B
+  // type B = A;
+  // var<storage, read> b : B
 
   auto* A = Structure("A", {Member("a", ty.i32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, A);
-  auto* B = ty.alias("B", ac);
+  auto* B = ty.alias("B", A);
   AST().AddConstructedType(B);
-  auto* var = Global("b", B, ast::StorageClass::kStorage,
+  auto* var = Global("b", B, ast::StorageClass::kStorage, ast::Access::kRead,
                      ast::DecorationList{
                          create<ast::BindingDecoration>(0),
                          create<ast::GroupDecoration>(0),
@@ -515,24 +512,22 @@
   // struct A {
   //   a : i32;
   // };
-  // var b : [[access(read)]] A
-  // var c : [[access(read_write)]] A
+  // var<storage, read> b : A
+  // var<storage, read_write> c : A
 
   auto* A = Structure("A", {Member("a", ty.i32())},
                       {create<ast::StructBlockDecoration>()});
-  auto* read = ty.access(ast::AccessControl::kRead, A);
-  auto* rw = ty.access(ast::AccessControl::kReadWrite, A);
-
-  auto* var_b = Global("b", read, ast::StorageClass::kStorage,
+  auto* var_b = Global("b", A, ast::StorageClass::kStorage, ast::Access::kRead,
                        ast::DecorationList{
                            create<ast::GroupDecoration>(0),
                            create<ast::BindingDecoration>(0),
                        });
-  auto* var_c = Global("c", rw, ast::StorageClass::kStorage,
-                       ast::DecorationList{
-                           create<ast::GroupDecoration>(1),
-                           create<ast::BindingDecoration>(0),
-                       });
+  auto* var_c =
+      Global("c", A, ast::StorageClass::kStorage, ast::Access::kReadWrite,
+             ast::DecorationList{
+                 create<ast::GroupDecoration>(1),
+                 create<ast::BindingDecoration>(0),
+             });
 
   spirv::Builder& b = Build();
 
@@ -562,14 +557,13 @@
 }
 
 TEST_F(BuilderTest, GlobalVar_TextureStorageReadOnly) {
-  // var<uniform_constant> a : [[access(read)]] texture_storage_2d<r32uint>;
+  // var<uniform_constant> a : texture_storage_2d<r32uint, read>;
 
-  auto* type = ty.storage_texture(ast::TextureDimension::k2d,
-                                  ast::ImageFormat::kR32Uint);
+  auto* type =
+      ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
+                         ast::Access::kRead);
 
-  auto* ac = ty.access(ast::AccessControl::kRead, type);
-
-  auto* var_a = Global("a", ac, ast::StorageClass::kNone,
+  auto* var_a = Global("a", type,
                        ast::DecorationList{
                            create<ast::BindingDecoration>(0),
                            create<ast::GroupDecoration>(0),
@@ -591,14 +585,13 @@
 }
 
 TEST_F(BuilderTest, GlobalVar_TextureStorageWriteOnly) {
-  // var<uniform_constant> a : [[access(write)]] texture_storage_2d<r32uint>;
+  // var<uniform_constant> a : texture_storage_2d<r32uint, write>;
 
-  auto* type = ty.storage_texture(ast::TextureDimension::k2d,
-                                  ast::ImageFormat::kR32Uint);
+  auto* type =
+      ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
+                         ast::Access::kWrite);
 
-  auto* ac = ty.access(ast::AccessControl::kWrite, type);
-
-  auto* var_a = Global("a", ac, ast::StorageClass::kNone,
+  auto* var_a = Global("a", type,
                        ast::DecorationList{
                            create<ast::BindingDecoration>(0),
                            create<ast::GroupDecoration>(0),
@@ -622,21 +615,21 @@
 // Check that multiple texture_storage types with different access modifiers
 // only produces a single OpTypeImage.
 TEST_F(BuilderTest, GlobalVar_TextureStorageWithDifferentAccess) {
-  // var<uniform_constant> a : [[access(read)]] texture_storage_2d<r32uint>;
-  // var<uniform_constant> b : [[access(write)]] texture_storage_2d<r32uint>;
+  // var<uniform_constant> a : texture_storage_2d<r32uint, read>;
+  // var<uniform_constant> b : texture_storage_2d<r32uint, write>;
 
-  auto* type_a = ty.access(ast::AccessControl::kRead,
-                           ty.storage_texture(ast::TextureDimension::k2d,
-                                              ast::ImageFormat::kR32Uint));
+  auto* type_a =
+      ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
+                         ast::Access::kRead);
   auto* var_a = Global("a", type_a, ast::StorageClass::kNone,
                        ast::DecorationList{
                            create<ast::BindingDecoration>(0),
                            create<ast::GroupDecoration>(0),
                        });
 
-  auto* type_b = ty.access(ast::AccessControl::kWrite,
-                           ty.storage_texture(ast::TextureDimension::k2d,
-                                              ast::ImageFormat::kR32Uint));
+  auto* type_b =
+      ty.storage_texture(ast::TextureDimension::k2d, ast::ImageFormat::kR32Uint,
+                         ast::Access::kWrite);
   auto* var_b = Global("b", type_b, ast::StorageClass::kNone,
                        ast::DecorationList{
                            create<ast::BindingDecoration>(1),
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 6a09d5c..ebc1de3 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -1537,8 +1537,7 @@
 TEST_F(IntrinsicBuilderTest, Call_ArrayLength) {
   auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))},
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-  Global("b", ac, ast::StorageClass::kStorage, nullptr,
+  Global("b", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
@@ -1587,8 +1586,7 @@
                           Member(4, "a", ty.array<f32>(4)),
                       },
                       {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
-  Global("b", ac, ast::StorageClass::kStorage, nullptr,
+  Global("b", s, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index 83168ca..2200436 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -30,8 +30,7 @@
   auto* ary = ty.array(ty.i32(), 0);
   auto* str = Structure("S", {Member("x", ary)},
                         {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, str);
-  Global("a", ac, ast::StorageClass::kStorage,
+  Global("a", str, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -52,8 +51,7 @@
   auto* ary = ty.array(ty.i32(), 0);
   auto* str = Structure("S", {Member("x", ary)},
                         {create<ast::StructBlockDecoration>()});
-  auto* ac = ty.access(ast::AccessControl::kRead, str);
-  Global("a", ac, ast::StorageClass::kStorage,
+  Global("a", str, ast::StorageClass::kStorage, ast::Access::kRead,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -831,10 +829,9 @@
 
 TEST_F(BuilderTest_Type, StorageTexture_Generate_1d) {
   auto* s = ty.storage_texture(ast::TextureDimension::k1d,
-                               ast::ImageFormat::kR32Float);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Float, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -851,10 +848,9 @@
 
 TEST_F(BuilderTest_Type, StorageTexture_Generate_2d) {
   auto* s = ty.storage_texture(ast::TextureDimension::k2d,
-                               ast::ImageFormat::kR32Float);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Float, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -871,10 +867,9 @@
 
 TEST_F(BuilderTest_Type, StorageTexture_Generate_2dArray) {
   auto* s = ty.storage_texture(ast::TextureDimension::k2dArray,
-                               ast::ImageFormat::kR32Float);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Float, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -891,10 +886,9 @@
 
 TEST_F(BuilderTest_Type, StorageTexture_Generate_3d) {
   auto* s = ty.storage_texture(ast::TextureDimension::k3d,
-                               ast::ImageFormat::kR32Float);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Float, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -912,10 +906,9 @@
 TEST_F(BuilderTest_Type,
        StorageTexture_Generate_SampledTypeFloat_Format_r32float) {
   auto* s = ty.storage_texture(ast::TextureDimension::k2d,
-                               ast::ImageFormat::kR32Float);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Float, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -933,10 +926,9 @@
 TEST_F(BuilderTest_Type,
        StorageTexture_Generate_SampledTypeSint_Format_r32sint) {
   auto* s = ty.storage_texture(ast::TextureDimension::k2d,
-                               ast::ImageFormat::kR32Sint);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Sint, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -954,10 +946,9 @@
 TEST_F(BuilderTest_Type,
        StorageTexture_Generate_SampledTypeUint_Format_r32uint) {
   auto* s = ty.storage_texture(ast::TextureDimension::k2d,
-                               ast::ImageFormat::kR32Uint);
-  auto* ac = ty.access(ast::AccessControl::kRead, s);
+                               ast::ImageFormat::kR32Uint, ast::Access::kRead);
 
-  Global("test_var", ac,
+  Global("test_var", s,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index 8bba93a..789bb51 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -17,7 +17,7 @@
 #include <algorithm>
 #include <limits>
 
-#include "src/ast/access_control.h"
+#include "src/ast/access.h"
 #include "src/ast/alias.h"
 #include "src/ast/array.h"
 #include "src/ast/bool.h"
@@ -355,25 +355,26 @@
   return true;
 }
 
-bool GeneratorImpl::EmitType(const ast::Type* ty) {
-  if (auto* ac = ty->As<ast::AccessControl>()) {
-    out_ << "[[access(";
-    if (ac->IsReadOnly()) {
+bool GeneratorImpl::EmitAccess(const ast::Access access) {
+  switch (access) {
+    case ast::Access::kRead:
       out_ << "read";
-    } else if (ac->IsWriteOnly()) {
+      return true;
+    case ast::Access::kWrite:
       out_ << "write";
-    } else if (ac->IsReadWrite()) {
+      return true;
+    case ast::Access::kReadWrite:
       out_ << "read_write";
-    } else {
-      diagnostics_.add_error("invalid access control");
-      return false;
-    }
-    out_ << ")]] ";
-    if (!EmitType(ac->type())) {
-      return false;
-    }
-    return true;
-  } else if (auto* alias = ty->As<ast::Alias>()) {
+      return true;
+    default:
+      break;
+  }
+  diagnostics_.add_error("unknown access");
+  return false;
+}
+
+bool GeneratorImpl::EmitType(const ast::Type* ty) {
+  if (auto* alias = ty->As<ast::Alias>()) {
     out_ << program_->Symbols().NameFor(alias->symbol());
   } else if (auto* ary = ty->As<ast::Array>()) {
     for (auto* deco : ary->decorations()) {
@@ -477,6 +478,10 @@
       if (!EmitImageFormat(storage->image_format())) {
         return false;
       }
+      out_ << ", ";
+      if (!EmitAccess(storage->access())) {
+        return false;
+      }
       out_ << ">";
     }
 
@@ -580,8 +585,16 @@
   } else {
     out_ << "var";
     auto sc = var->declared_storage_class();
-    if (sc != ast::StorageClass::kNone) {
-      out_ << "<" << sc << ">";
+    auto ac = var->declared_access();
+    if (sc != ast::StorageClass::kNone || ac != ast::Access::kUndefined) {
+      out_ << "<" << sc;
+      if (ac != ast::Access::kUndefined) {
+        out_ << ", ";
+        if (!EmitAccess(ac)) {
+          return false;
+        }
+      }
+      out_ << ">";
     }
   }
 
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index ecb17a5..1017bae 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -174,6 +174,10 @@
   /// @param fmt the format to generate
   /// @returns true if the format is emitted
   bool EmitImageFormat(const ast::ImageFormat fmt);
+  /// Handles emitting an access control
+  /// @param access the access to generate
+  /// @returns true if the access is emitted
+  bool EmitAccess(const ast::Access access);
   /// Handles emitting a type constructor
   /// @param expr the type constructor expression
   /// @returns true if the constructor is emitted
diff --git a/src/writer/wgsl/generator_impl_function_test.cc b/src/writer/wgsl/generator_impl_function_test.cc
index 97bf435..5bb305a 100644
--- a/src/writer/wgsl/generator_impl_function_test.cc
+++ b/src/writer/wgsl/generator_impl_function_test.cc
@@ -175,9 +175,7 @@
   auto* s = Structure("Data", {Member("d", ty.f32())},
                       {create<ast::StructBlockDecoration>()});
 
-  auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
-
-  Global("data", ac, ast::StorageClass::kStorage, nullptr,
+  Global("data", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
          ast::DecorationList{
              create<ast::BindingDecoration>(0),
              create<ast::GroupDecoration>(0),
@@ -219,7 +217,7 @@
   d : f32;
 };
 
-[[binding(0), group(0)]] var<storage> data : [[access(read_write)]] Data;
+[[binding(0), group(0)]] var<storage, read_write> data : Data;
 
 [[stage(compute)]]
 fn a() {
diff --git a/src/writer/wgsl/generator_impl_global_decl_test.cc b/src/writer/wgsl/generator_impl_global_decl_test.cc
index 45bb89f..5954621 100644
--- a/src/writer/wgsl/generator_impl_global_decl_test.cc
+++ b/src/writer/wgsl/generator_impl_global_decl_test.cc
@@ -116,7 +116,7 @@
 
 TEST_F(WgslGeneratorImplTest, Emit_Global_Texture) {
   auto* st = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
-  Global("t", ty.access(ast::AccessControl::kRead, st),
+  Global("t", st,
          ast::DecorationList{
              create<ast::GroupDecoration>(0),
              create<ast::BindingDecoration>(0),
@@ -127,9 +127,8 @@
   gen.increment_indent();
 
   ASSERT_TRUE(gen.Generate()) << gen.error();
-  EXPECT_EQ(
-      gen.result(),
-      "  [[group(0), binding(0)]] var t : [[access(read)]] texture_1d<f32>;\n");
+  EXPECT_EQ(gen.result(),
+            "  [[group(0), binding(0)]] var t : texture_1d<f32>;\n");
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_OverridableConstants) {
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc
index d217bfb..5bb5abd 100644
--- a/src/writer/wgsl/generator_impl_type_test.cc
+++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -45,32 +45,6 @@
   EXPECT_EQ(gen.result(), "array<bool, 4>");
 }
 
-TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_Read) {
-  auto* s = Structure("S", {Member("a", ty.i32())},
-                      {create<ast::StructBlockDecoration>()});
-
-  auto* a = ty.access(ast::AccessControl::kRead, s);
-  AST().AddConstructedType(ty.alias("make_type_reachable", a));
-
-  GeneratorImpl& gen = Build();
-
-  ASSERT_TRUE(gen.EmitType(a)) << gen.error();
-  EXPECT_EQ(gen.result(), "[[access(read)]] S");
-}
-
-TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_ReadWrite) {
-  auto* s = Structure("S", {Member("a", ty.i32())},
-                      {create<ast::StructBlockDecoration>()});
-
-  auto* a = ty.access(ast::AccessControl::kReadWrite, s);
-  AST().AddConstructedType(ty.alias("make_type_reachable", a));
-
-  GeneratorImpl& gen = Build();
-
-  ASSERT_TRUE(gen.EmitType(a)) << gen.error();
-  EXPECT_EQ(gen.result(), "[[access(read_write)]] S");
-}
-
 TEST_F(WgslGeneratorImplTest, EmitType_Array_Decoration) {
   auto* a = ty.array(ty.bool_(), 4, 16u);
   AST().AddConstructedType(ty.alias("make_type_reachable", a));
@@ -416,7 +390,7 @@
 struct StorageTextureData {
   ast::ImageFormat fmt;
   ast::TextureDimension dim;
-  ast::AccessControl::Access access;
+  ast::Access access;
   const char* name;
 };
 inline std::ostream& operator<<(std::ostream& out, StorageTextureData data) {
@@ -427,12 +401,11 @@
 TEST_P(WgslGenerator_StorageTextureTest, EmitType_StorageTexture) {
   auto param = GetParam();
 
-  auto* t = ty.storage_texture(param.dim, param.fmt);
-  auto* ac = ty.access(param.access, t);
+  auto* t = ty.storage_texture(param.dim, param.fmt, param.access);
 
   GeneratorImpl& gen = Build();
 
-  ASSERT_TRUE(gen.EmitType(ac)) << gen.error();
+  ASSERT_TRUE(gen.EmitType(t)) << gen.error();
   EXPECT_EQ(gen.result(), param.name);
 }
 INSTANTIATE_TEST_SUITE_P(
@@ -440,37 +413,29 @@
     WgslGenerator_StorageTextureTest,
     testing::Values(
         StorageTextureData{ast::ImageFormat::kR8Unorm,
-                           ast::TextureDimension::k1d,
-                           ast::AccessControl::kRead,
-                           "[[access(read)]] texture_storage_1d<r8unorm>"},
+                           ast::TextureDimension::k1d, ast::Access::kRead,
+                           "texture_storage_1d<r8unorm, read>"},
         StorageTextureData{ast::ImageFormat::kR8Unorm,
-                           ast::TextureDimension::k2d,
-                           ast::AccessControl::kRead,
-                           "[[access(read)]] texture_storage_2d<r8unorm>"},
-        StorageTextureData{
-            ast::ImageFormat::kR8Unorm, ast::TextureDimension::k2dArray,
-            ast::AccessControl::kRead,
-            "[[access(read)]] texture_storage_2d_array<r8unorm>"},
+                           ast::TextureDimension::k2d, ast::Access::kRead,
+                           "texture_storage_2d<r8unorm, read>"},
         StorageTextureData{ast::ImageFormat::kR8Unorm,
-                           ast::TextureDimension::k3d,
-                           ast::AccessControl::kRead,
-                           "[[access(read)]] texture_storage_3d<r8unorm>"},
+                           ast::TextureDimension::k2dArray, ast::Access::kRead,
+                           "texture_storage_2d_array<r8unorm, read>"},
         StorageTextureData{ast::ImageFormat::kR8Unorm,
-                           ast::TextureDimension::k1d,
-                           ast::AccessControl::kWrite,
-                           "[[access(write)]] texture_storage_1d<r8unorm>"},
+                           ast::TextureDimension::k3d, ast::Access::kRead,
+                           "texture_storage_3d<r8unorm, read>"},
         StorageTextureData{ast::ImageFormat::kR8Unorm,
-                           ast::TextureDimension::k2d,
-                           ast::AccessControl::kWrite,
-                           "[[access(write)]] texture_storage_2d<r8unorm>"},
-        StorageTextureData{
-            ast::ImageFormat::kR8Unorm, ast::TextureDimension::k2dArray,
-            ast::AccessControl::kWrite,
-            "[[access(write)]] texture_storage_2d_array<r8unorm>"},
+                           ast::TextureDimension::k1d, ast::Access::kWrite,
+                           "texture_storage_1d<r8unorm, write>"},
         StorageTextureData{ast::ImageFormat::kR8Unorm,
-                           ast::TextureDimension::k3d,
-                           ast::AccessControl::kWrite,
-                           "[[access(write)]] texture_storage_3d<r8unorm>"}));
+                           ast::TextureDimension::k2d, ast::Access::kWrite,
+                           "texture_storage_2d<r8unorm, write>"},
+        StorageTextureData{ast::ImageFormat::kR8Unorm,
+                           ast::TextureDimension::k2dArray, ast::Access::kWrite,
+                           "texture_storage_2d_array<r8unorm, write>"},
+        StorageTextureData{ast::ImageFormat::kR8Unorm,
+                           ast::TextureDimension::k3d, ast::Access::kWrite,
+                           "texture_storage_3d<r8unorm, write>"}));
 
 struct ImageFormatData {
   ast::ImageFormat fmt;
diff --git a/src/writer/wgsl/generator_impl_variable_test.cc b/src/writer/wgsl/generator_impl_variable_test.cc
index 0605aac..8b51c3a 100644
--- a/src/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_test.cc
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "src/ast/override_decoration.h"
+#include "src/ast/struct_block_decoration.h"
 #include "src/writer/wgsl/test_helper.h"
 
 namespace tint {
@@ -42,6 +43,54 @@
 )");
 }
 
+TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Read) {
+  auto* s = Structure("S", {Member("a", ty.i32())},
+                      {create<ast::StructBlockDecoration>()});
+  auto* v = Global("a", s, ast::StorageClass::kStorage, ast::Access::kRead,
+                   ast::DecorationList{
+                       create<ast::BindingDecoration>(0),
+                       create<ast::GroupDecoration>(0),
+                   });
+
+  GeneratorImpl& gen = Build();
+
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
+  EXPECT_EQ(gen.result(), R"([[binding(0), group(0)]] var<storage, read> a : S;
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Write) {
+  auto* s = Structure("S", {Member("a", ty.i32())},
+                      {create<ast::StructBlockDecoration>()});
+  auto* v = Global("a", s, ast::StorageClass::kStorage, ast::Access::kWrite,
+                   ast::DecorationList{
+                       create<ast::BindingDecoration>(0),
+                       create<ast::GroupDecoration>(0),
+                   });
+
+  GeneratorImpl& gen = Build();
+
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
+  EXPECT_EQ(gen.result(), R"([[binding(0), group(0)]] var<storage, write> a : S;
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, EmitVariable_Access_ReadWrite) {
+  auto* s = Structure("S", {Member("a", ty.i32())},
+                      {create<ast::StructBlockDecoration>()});
+  auto* v = Global("a", s, ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                   ast::DecorationList{
+                       create<ast::BindingDecoration>(0),
+                       create<ast::GroupDecoration>(0),
+                   });
+
+  GeneratorImpl& gen = Build();
+
+  ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
+  EXPECT_EQ(gen.result(),
+            R"([[binding(0), group(0)]] var<storage, read_write> a : S;
+)");
+}
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated) {
   auto* v = Global("a", ty.f32(), ast::StorageClass::kPrivate, nullptr,
                    ast::DecorationList{
@@ -65,9 +114,8 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
-  EXPECT_EQ(
-      gen.result(),
-      R"([[builtin(position), location(2)]] var<private> a : f32;
+  EXPECT_EQ(gen.result(),
+            R"([[builtin(position), location(2)]] var<private> a : f32;
 )");
 }