Import Tint changes from Dawn

Changes: - 8800d885e76533b27ef1bfd791863a0e21335717 tint: const eval of 'select' builtin by Antonio Maiorano <amaiorano@google.com>
  - 6b3f4aaf2662a6a9afaa37781de5fe77e082dce7 tint: add vector cases for const eval builtin tests by Antonio Maiorano <amaiorano@google.com>
  - fc7994ba4c3cf6eac3dc75cf80f6c88b20fbf0ee tint: fix intrinsic table lookup of mixed const and non-c... by Antonio Maiorano <amaiorano@google.com>
  - 7a69a3411f1147f90d7669da153b51e770083d7c Update type_decl grammar element name. by dan sinclair <dsinclair@chromium.org>
  - 1dbda6f254884aaec4d8c50160b70190d015b4ec Update variables in address space template. by dan sinclair <dsinclair@chromium.org>
  - e89c27c648436e3470a27d628aea9a1a2c2e7312 Remove some DISABLED tests. by dan sinclair <dsinclair@chromium.org>
  - 82023d6980e116eefd66217fcac12cee3e4d756b Enable disabled const eval tests. by dan sinclair <dsinclair@chromium.org>
  - 2a3eae73719d35189267a31bfd2083e87d365dec Revert "Replace std::stof with std::strtof." by Dan Sinclair <dsinclair@chromium.org>
  - 40b5d01b065d504a7c221b7b03801abc42d158f8 Remove unused Resolver::TypeDeclInfo. by dan sinclair <dsinclair@chromium.org>
  - bdeee03c0115e28ac706cdc7e5963c528b3b3bf3 Remove unused Resolver::BlockInfo by dan sinclair <dsinclair@chromium.org>
  - 6f39b6c416d3637da27d391cfd4c3bad865f4cb0 Remove empty disabled generator tests. by dan sinclair <dsinclair@chromium.org>
  - 26aa5820546a1d979c2c598619923b398ea298a8 Remove Inspector test for unsupported feature. by dan sinclair <dsinclair@chromium.org>
  - ca6fedc96a7521042fcf6d42daffa4fd25b6116e Rename `array size` to `array count` in error messages. by dan sinclair <dsinclair@chromium.org>
  - 68124ab7d1467f74afc2edd51614c95f351aeb30 Add missing robustness tests. by dan sinclair <dsinclair@chromium.org>
  - d4fa2a022e137eae64d5a89808cecc23d2830ace Enable DISABLED const evaluation tests. by dan sinclair <dsinclair@chromium.org>
  - e67c9f94d337b804373cc6c375b4f78990424a1d Remove disabled float inf and nan tests. by dan sinclair <dsinclair@chromium.org>
  - d94a10ba7e164630ea97ffb2d78a1bf37282d7d7 Re-enable spirv reader tests after spirv-tools roll. by dan sinclair <dsinclair@chromium.org>
  - 01b885bfde15d940d88782145b4d7c5f9e9144ce Fix comment in spirv-reader. by dan sinclair <dsinclair@chromium.org>
  - 66d3efb669c71cd8699ee3e603756b584ef47a05 Limit array element count to less then 65536 elements. by dan sinclair <dsinclair@chromium.org>
  - 5f9996dc9c4c057a6d54d4332d4e4baf2ea904a1 Replace std::stof with std::strtof. by John Stiles <johnstiles@google.com>
  - ff7cf2102153bf93af93d65ce20a9b406df64cda Rename StorageClass to AddressSpace. by dan sinclair <dsinclair@chromium.org>
  - 82177da76789ed3f5e58630cd79123367a0e465f spirv-reader: add ColMajor decoration as needed for end2end by David Neto <dneto@google.com>
  - feb14eb7613436bf5fe5ff46ae312523ebf23e85 tint: update spirv-tools and fix unittests by dawn-autoroll <dawn-autoroll@skia-public.iam.gserviceaccount.com>
  - 2bcade246a80151649d2fe87eb6f031e657cd5f1 Update MSL generator to handle casts of packed types. by dan sinclair <dsinclair@chromium.org>
  - 06844a5e24af9c880d76ae84595300995dc8fe6b tint/hlsl: remove [loop] attribute on all loops by Antonio Maiorano <amaiorano@google.com>
  - 2af7ab3b168e5239b65858251da93670357a8312 Add missing operator to regex fuzzer by Alastair Donaldson <afdx@google.com>
  - f9f583bfe4a3b738161b2f551edc500a85902745 Update align attribute to allow u32. by dan sinclair <dsinclair@chromium.org>
  - a33bc2c6f2a657e4f91f27534da640aa063c4892 Add some GLSL zero initialization tests. by dan sinclair <dsinclair@chromium.org>
  - 764a2abc90312cdb3f982591072c440017a9ada6 Have DeepestElementOf always return a type. by dan sinclair <dsinclair@chromium.org>
  - cb41b8f97f12341af2c5667b3c059767fa14b2de Convert `align` attribute to expressions. by dan sinclair <dsinclair@chromium.org>
  - 4c70d7fff23f36a371ccbfe6fdf9e7d9817a62e3 tint: Make uniformity analysis failures a hard error by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 8800d885e76533b27ef1bfd791863a0e21335717
Change-Id: I83b3a10d6ed00e763c1bac3de95f7a1c4927d776
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/103960
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/DEPS b/DEPS
index 7bf1906..60c7a96 100644
--- a/DEPS
+++ b/DEPS
@@ -21,7 +21,7 @@
   },
 
   'third_party/vulkan-deps': {
-    'url': '{chromium_git}/vulkan-deps@20efc30b0c6fe3c9bbd4f8ed6335593ee51391b0',
+    'url': '{chromium_git}/vulkan-deps@5d66d3e5917b6dfdafce9cfeaaf6041e74ae5e0d',
   },
 
   # Dependencies required to use GN/Clang in standalone
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 7fcc8c1..07c5350 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -180,6 +180,8 @@
   sources = [
     "ast/access.cc",
     "ast/access.h",
+    "ast/address_space.cc",
+    "ast/address_space.h",
     "ast/alias.cc",
     "ast/alias.h",
     "ast/array.cc",
@@ -312,8 +314,6 @@
     "ast/statement.h",
     "ast/static_assert.cc",
     "ast/static_assert.h",
-    "ast/storage_class.cc",
-    "ast/storage_class.h",
     "ast/storage_texture.cc",
     "ast/storage_texture.h",
     "ast/stride_attribute.cc",
@@ -998,6 +998,7 @@
 
   tint_unittests_source_set("tint_unittests_ast_src") {
     sources = [
+      "ast/address_space_test.cc",
       "ast/alias_test.cc",
       "ast/array_test.cc",
       "ast/assignment_statement_test.cc",
@@ -1054,7 +1055,6 @@
       "ast/sampler_test.cc",
       "ast/stage_attribute_test.cc",
       "ast/static_assert_test.cc",
-      "ast/storage_class_test.cc",
       "ast/storage_texture_test.cc",
       "ast/stride_attribute_test.cc",
       "ast/struct_member_align_attribute_test.cc",
@@ -1097,6 +1097,8 @@
 
   tint_unittests_source_set("tint_unittests_resolver_src") {
     sources = [
+      "resolver/address_space_layout_validation_test.cc",
+      "resolver/address_space_validation_test.cc",
       "resolver/array_accessor_test.cc",
       "resolver/assignment_validation_test.cc",
       "resolver/atomics_test.cc",
@@ -1133,11 +1135,9 @@
       "resolver/side_effects_test.cc",
       "resolver/source_variable_test.cc",
       "resolver/static_assert_test.cc",
-      "resolver/storage_class_layout_validation_test.cc",
-      "resolver/storage_class_validation_test.cc",
+      "resolver/struct_address_space_use_test.cc",
       "resolver/struct_layout_test.cc",
       "resolver/struct_pipeline_stage_use_test.cc",
-      "resolver/struct_storage_class_use_test.cc",
       "resolver/type_constructor_validation_test.cc",
       "resolver/type_validation_test.cc",
       "resolver/uniformity_test.cc",
@@ -1354,6 +1354,7 @@
     sources = [
       "reader/wgsl/lexer_test.cc",
       "reader/wgsl/parser_impl_additive_expression_test.cc",
+      "reader/wgsl/parser_impl_address_space_test.cc",
       "reader/wgsl/parser_impl_argument_expression_list_test.cc",
       "reader/wgsl/parser_impl_assignment_stmt_test.cc",
       "reader/wgsl/parser_impl_bitwise_expression_test.cc",
@@ -1400,7 +1401,6 @@
       "reader/wgsl/parser_impl_singular_expression_test.cc",
       "reader/wgsl/parser_impl_statement_test.cc",
       "reader/wgsl/parser_impl_statements_test.cc",
-      "reader/wgsl/parser_impl_storage_class_test.cc",
       "reader/wgsl/parser_impl_storage_texture_test.cc",
       "reader/wgsl/parser_impl_struct_attribute_decl_test.cc",
       "reader/wgsl/parser_impl_struct_body_decl_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index bcb4d4d..10b0686 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -182,8 +182,8 @@
   ast/statement.h
   ast/static_assert.cc
   ast/static_assert.h
-  ast/storage_class.cc
-  ast/storage_class.h
+  ast/address_space.cc
+  ast/address_space.h
   ast/storage_texture.cc
   ast/storage_texture.h
   ast/stride_attribute.cc
@@ -747,7 +747,7 @@
     ast/sampler_test.cc
     ast/stage_attribute_test.cc
     ast/static_assert_test.cc
-    ast/storage_class_test.cc
+    ast/address_space_test.cc
     ast/storage_texture_test.cc
     ast/stride_attribute_test.cc
     ast/struct_member_align_attribute_test.cc
@@ -814,11 +814,11 @@
     resolver/side_effects_test.cc
     resolver/static_assert_test.cc
     resolver/source_variable_test.cc
-    resolver/storage_class_layout_validation_test.cc
-    resolver/storage_class_validation_test.cc
+    resolver/address_space_layout_validation_test.cc
+    resolver/address_space_validation_test.cc
     resolver/struct_layout_test.cc
     resolver/struct_pipeline_stage_use_test.cc
-    resolver/struct_storage_class_use_test.cc
+    resolver/struct_address_space_use_test.cc
     resolver/type_constructor_validation_test.cc
     resolver/type_validation_test.cc
     resolver/validation_test.cc
@@ -992,7 +992,7 @@
       reader/wgsl/parser_impl_singular_expression_test.cc
       reader/wgsl/parser_impl_statement_test.cc
       reader/wgsl/parser_impl_statements_test.cc
-      reader/wgsl/parser_impl_storage_class_test.cc
+      reader/wgsl/parser_impl_address_space_test.cc
       reader/wgsl/parser_impl_storage_texture_test.cc
       reader/wgsl/parser_impl_struct_body_decl_test.cc
       reader/wgsl/parser_impl_struct_decl_test.cc
@@ -1300,7 +1300,7 @@
   set(TINT_BENCHMARK_SRC
     "castable_bench.cc"
     "ast/extension_bench.cc"
-    "ast/storage_class_bench.cc"
+    "ast/address_space_bench.cc"
     "ast/texel_format_bench.cc"
     "bench/benchmark.cc"
     "reader/wgsl/parser_bench.cc"
diff --git a/src/tint/ast/storage_class.cc b/src/tint/ast/address_space.cc
similarity index 62%
rename from src/tint/ast/storage_class.cc
rename to src/tint/ast/address_space.cc
index 450067b..a55fb87 100644
--- a/src/tint/ast/storage_class.cc
+++ b/src/tint/ast/address_space.cc
@@ -15,63 +15,63 @@
 ////////////////////////////////////////////////////////////////////////////////
 // File generated by tools/src/cmd/gen
 // using the template:
-//   src/tint/ast/storage_class.cc.tmpl
+//   src/tint/ast/address_space.cc.tmpl
 //
 // Do not modify this file directly
 ////////////////////////////////////////////////////////////////////////////////
 
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 
 namespace tint::ast {
 
-/// ParseStorageClass parses a StorageClass from a string.
+/// ParseAddressSpace parses a AddressSpace from a string.
 /// @param str the string to parse
-/// @returns the parsed enum, or StorageClass::kInvalid if the string could not be parsed.
-StorageClass ParseStorageClass(std::string_view str) {
+/// @returns the parsed enum, or AddressSpace::kInvalid if the string could not be parsed.
+AddressSpace ParseAddressSpace(std::string_view str) {
     if (str == "function") {
-        return StorageClass::kFunction;
+        return AddressSpace::kFunction;
     }
     if (str == "private") {
-        return StorageClass::kPrivate;
+        return AddressSpace::kPrivate;
     }
     if (str == "workgroup") {
-        return StorageClass::kWorkgroup;
+        return AddressSpace::kWorkgroup;
     }
     if (str == "uniform") {
-        return StorageClass::kUniform;
+        return AddressSpace::kUniform;
     }
     if (str == "storage") {
-        return StorageClass::kStorage;
+        return AddressSpace::kStorage;
     }
     if (str == "push_constant") {
-        return StorageClass::kPushConstant;
+        return AddressSpace::kPushConstant;
     }
-    return StorageClass::kInvalid;
+    return AddressSpace::kInvalid;
 }
 
-std::ostream& operator<<(std::ostream& out, StorageClass value) {
+std::ostream& operator<<(std::ostream& out, AddressSpace value) {
     switch (value) {
-        case StorageClass::kInvalid:
+        case AddressSpace::kInvalid:
             return out << "invalid";
-        case StorageClass::kNone:
+        case AddressSpace::kNone:
             return out << "none";
-        case StorageClass::kFunction:
+        case AddressSpace::kFunction:
             return out << "function";
-        case StorageClass::kPrivate:
+        case AddressSpace::kPrivate:
             return out << "private";
-        case StorageClass::kWorkgroup:
+        case AddressSpace::kWorkgroup:
             return out << "workgroup";
-        case StorageClass::kUniform:
+        case AddressSpace::kUniform:
             return out << "uniform";
-        case StorageClass::kStorage:
+        case AddressSpace::kStorage:
             return out << "storage";
-        case StorageClass::kPushConstant:
+        case AddressSpace::kPushConstant:
             return out << "push_constant";
-        case StorageClass::kHandle:
+        case AddressSpace::kHandle:
             return out << "handle";
-        case StorageClass::kIn:
+        case AddressSpace::kIn:
             return out << "in";
-        case StorageClass::kOut:
+        case AddressSpace::kOut:
             return out << "out";
     }
     return out << "<unknown>";
diff --git a/src/tint/ast/storage_class.cc.tmpl b/src/tint/ast/address_space.cc.tmpl
similarity index 77%
rename from src/tint/ast/storage_class.cc.tmpl
rename to src/tint/ast/address_space.cc.tmpl
index e2903b7..7295f14 100644
--- a/src/tint/ast/storage_class.cc.tmpl
+++ b/src/tint/ast/address_space.cc.tmpl
@@ -1,6 +1,6 @@
 {{- /*
 --------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate storage_class.cc
+Template file for use with tools/src/cmd/gen to generate address_space.cc
 
 To update the generated file, run:
     ./tools/run gen
@@ -12,9 +12,9 @@
 */ -}}
 
 {{- Import "src/tint/templates/enums.tmpl.inc" -}}
-{{- $enum := (Sem.Enum "storage_class") -}}
+{{- $enum := (Sem.Enum "address_space") -}}
 
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 
 namespace tint::ast {
 
diff --git a/src/tint/ast/storage_class.h b/src/tint/ast/address_space.h
similarity index 63%
rename from src/tint/ast/storage_class.h
rename to src/tint/ast/address_space.h
index 43e41e1..210dc7f 100644
--- a/src/tint/ast/storage_class.h
+++ b/src/tint/ast/address_space.h
@@ -15,20 +15,20 @@
 ////////////////////////////////////////////////////////////////////////////////
 // File generated by tools/src/cmd/gen
 // using the template:
-//   src/tint/ast/storage_class.h.tmpl
+//   src/tint/ast/address_space.h.tmpl
 //
 // Do not modify this file directly
 ////////////////////////////////////////////////////////////////////////////////
 
-#ifndef SRC_TINT_AST_STORAGE_CLASS_H_
-#define SRC_TINT_AST_STORAGE_CLASS_H_
+#ifndef SRC_TINT_AST_ADDRESS_SPACE_H_
+#define SRC_TINT_AST_ADDRESS_SPACE_H_
 
 #include <ostream>
 
 namespace tint::ast {
 
-/// Storage class of a given pointer.
-enum class StorageClass {
+/// Address space of a given pointer.
+enum class AddressSpace {
     kInvalid,
     kNone,  // Tint-internal enum entry - not parsed
     kFunction,
@@ -43,23 +43,24 @@
 };
 
 /// @param out the std::ostream to write to
-/// @param value the StorageClass
+/// @param value the AddressSpace
 /// @returns `out` so calls can be chained
-std::ostream& operator<<(std::ostream& out, StorageClass value);
+std::ostream& operator<<(std::ostream& out, AddressSpace value);
 
-/// ParseStorageClass parses a StorageClass from a string.
+/// ParseAddressSpace parses a AddressSpace from a string.
 /// @param str the string to parse
-/// @returns the parsed enum, or StorageClass::kInvalid if the string could not be parsed.
-StorageClass ParseStorageClass(std::string_view str);
+/// @returns the parsed enum, or AddressSpace::kInvalid if the string could not be parsed.
+AddressSpace ParseAddressSpace(std::string_view str);
 
-/// @returns true if the StorageClass is host-shareable
-/// @param sc the StorageClass
+/// @returns true if the AddressSpace is host-shareable
+/// @param address_space the AddressSpace
 /// @see https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable
-inline bool IsHostShareable(StorageClass sc) {
-    return sc == ast::StorageClass::kUniform || sc == ast::StorageClass::kStorage ||
-           sc == ast::StorageClass::kPushConstant;
+inline bool IsHostShareable(AddressSpace address_space) {
+    return address_space == ast::AddressSpace::kUniform ||
+           address_space == ast::AddressSpace::kStorage ||
+           address_space == ast::AddressSpace::kPushConstant;
 }
 
 }  // namespace tint::ast
 
-#endif  // SRC_TINT_AST_STORAGE_CLASS_H_
+#endif  // SRC_TINT_AST_ADDRESS_SPACE_H_
diff --git a/src/tint/ast/address_space.h.tmpl b/src/tint/ast/address_space.h.tmpl
new file mode 100644
index 0000000..0726ef3
--- /dev/null
+++ b/src/tint/ast/address_space.h.tmpl
@@ -0,0 +1,37 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate address_space.h
+
+To update the generated file, run:
+    ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "address_space") -}}
+
+#ifndef SRC_TINT_AST_ADDRESS_SPACE_H_
+#define SRC_TINT_AST_ADDRESS_SPACE_H_
+
+#include <ostream>
+
+namespace tint::ast {
+
+/// Address space of a given pointer.
+{{ Eval "DeclareEnum" $enum}}
+
+/// @returns true if the AddressSpace is host-shareable
+/// @param address_space the AddressSpace
+/// @see https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable
+inline bool IsHostShareable(AddressSpace address_space) {
+    return address_space == ast::AddressSpace::kUniform || address_space == ast::AddressSpace::kStorage ||
+           address_space == ast::AddressSpace::kPushConstant;
+}
+
+}  // namespace tint::ast
+
+#endif  // SRC_TINT_AST_ADDRESS_SPACE_H_
diff --git a/src/tint/ast/storage_class_bench.cc b/src/tint/ast/address_space_bench.cc
similarity index 89%
rename from src/tint/ast/storage_class_bench.cc
rename to src/tint/ast/address_space_bench.cc
index 00095c2..364d015 100644
--- a/src/tint/ast/storage_class_bench.cc
+++ b/src/tint/ast/address_space_bench.cc
@@ -15,12 +15,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 // File generated by tools/src/cmd/gen
 // using the template:
-//   src/tint/ast/storage_class_bench.cc.tmpl
+//   src/tint/ast/address_space_bench.cc.tmpl
 //
 // Do not modify this file directly
 ////////////////////////////////////////////////////////////////////////////////
 
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 
 #include <array>
 
@@ -29,7 +29,7 @@
 namespace tint::ast {
 namespace {
 
-void StorageClassParser(::benchmark::State& state) {
+void AddressSpaceParser(::benchmark::State& state) {
     std::array kStrings{
         "fccnctin",
         "ucti3",
@@ -76,13 +76,13 @@
     };
     for (auto _ : state) {
         for (auto& str : kStrings) {
-            auto result = ParseStorageClass(str);
+            auto result = ParseAddressSpace(str);
             benchmark::DoNotOptimize(result);
         }
     }
 }
 
-BENCHMARK(StorageClassParser);
+BENCHMARK(AddressSpaceParser);
 
 }  // namespace
 }  // namespace tint::ast
diff --git a/src/tint/ast/storage_class_bench.cc.tmpl b/src/tint/ast/address_space_bench.cc.tmpl
similarity index 78%
rename from src/tint/ast/storage_class_bench.cc.tmpl
rename to src/tint/ast/address_space_bench.cc.tmpl
index d9ea8cb..4644944 100644
--- a/src/tint/ast/storage_class_bench.cc.tmpl
+++ b/src/tint/ast/address_space_bench.cc.tmpl
@@ -1,6 +1,6 @@
 {{- /*
 --------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate storage_class_bench.cc
+Template file for use with tools/src/cmd/gen to generate address_space_bench.cc
 
 To update the generated file, run:
     ./tools/run gen
@@ -12,9 +12,9 @@
 */ -}}
 
 {{- Import "src/tint/templates/enums.tmpl.inc" -}}
-{{- $enum := (Sem.Enum "storage_class") -}}
+{{- $enum := (Sem.Enum "address_space") -}}
 
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 
 #include <array>
 
diff --git a/src/tint/ast/address_space_test.cc b/src/tint/ast/address_space_test.cc
new file mode 100644
index 0000000..86a8e40
--- /dev/null
+++ b/src/tint/ast/address_space_test.cc
@@ -0,0 +1,86 @@
+// Copyright 2022 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/ast/address_space_test.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/ast/address_space.h"
+
+#include <string>
+
+#include "src/tint/ast/test_helper.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::ast {
+namespace {
+
+namespace parse_print_tests {
+
+struct Case {
+    const char* string;
+    AddressSpace value;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+    return out << "'" << std::string(c.string) << "'";
+}
+
+static constexpr Case kValidCases[] = {
+    {"function", AddressSpace::kFunction},   {"private", AddressSpace::kPrivate},
+    {"workgroup", AddressSpace::kWorkgroup}, {"uniform", AddressSpace::kUniform},
+    {"storage", AddressSpace::kStorage},     {"push_constant", AddressSpace::kPushConstant},
+};
+
+static constexpr Case kInvalidCases[] = {
+    {"fccnctin", AddressSpace::kInvalid},       {"ucti3", AddressSpace::kInvalid},
+    {"functVon", AddressSpace::kInvalid},       {"priv1te", AddressSpace::kInvalid},
+    {"pqiJate", AddressSpace::kInvalid},        {"privat7ll", AddressSpace::kInvalid},
+    {"workroppqHH", AddressSpace::kInvalid},    {"workru", AddressSpace::kInvalid},
+    {"wbkgGoup", AddressSpace::kInvalid},       {"unifiivm", AddressSpace::kInvalid},
+    {"8WWiform", AddressSpace::kInvalid},       {"uxxform", AddressSpace::kInvalid},
+    {"sXraggg", AddressSpace::kInvalid},        {"traXe", AddressSpace::kInvalid},
+    {"stor3ge", AddressSpace::kInvalid},        {"push_constanE", AddressSpace::kInvalid},
+    {"push_TTPnstant", AddressSpace::kInvalid}, {"puxxdh_constan", AddressSpace::kInvalid},
+};
+
+using AddressSpaceParseTest = testing::TestWithParam<Case>;
+
+TEST_P(AddressSpaceParseTest, Parse) {
+    const char* string = GetParam().string;
+    AddressSpace expect = GetParam().value;
+    EXPECT_EQ(expect, ParseAddressSpace(string));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, AddressSpaceParseTest, testing::ValuesIn(kValidCases));
+INSTANTIATE_TEST_SUITE_P(InvalidCases, AddressSpaceParseTest, testing::ValuesIn(kInvalidCases));
+
+using AddressSpacePrintTest = testing::TestWithParam<Case>;
+
+TEST_P(AddressSpacePrintTest, Print) {
+    AddressSpace value = GetParam().value;
+    const char* expect = GetParam().string;
+    EXPECT_EQ(expect, utils::ToString(value));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, AddressSpacePrintTest, testing::ValuesIn(kValidCases));
+
+}  // namespace parse_print_tests
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/storage_class_test.cc.tmpl b/src/tint/ast/address_space_test.cc.tmpl
similarity index 79%
rename from src/tint/ast/storage_class_test.cc.tmpl
rename to src/tint/ast/address_space_test.cc.tmpl
index 3696aab..2967f73 100644
--- a/src/tint/ast/storage_class_test.cc.tmpl
+++ b/src/tint/ast/address_space_test.cc.tmpl
@@ -1,6 +1,6 @@
 {{- /*
 --------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate storage_class_test.cc
+Template file for use with tools/src/cmd/gen to generate address_space_test.cc
 
 To update the generated file, run:
     ./tools/run gen
@@ -12,9 +12,9 @@
 */ -}}
 
 {{- Import "src/tint/templates/enums.tmpl.inc" -}}
-{{- $enum := (Sem.Enum "storage_class") -}}
+{{- $enum := (Sem.Enum "address_space") -}}
 
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 
 #include <string>
 
diff --git a/src/tint/ast/disable_validation_attribute.cc b/src/tint/ast/disable_validation_attribute.cc
index 1f58799..2ae8c16 100644
--- a/src/tint/ast/disable_validation_attribute.cc
+++ b/src/tint/ast/disable_validation_attribute.cc
@@ -33,8 +33,8 @@
             return "disable_validation__function_has_no_body";
         case DisabledValidation::kBindingPointCollision:
             return "disable_validation__binding_point_collision";
-        case DisabledValidation::kIgnoreStorageClass:
-            return "disable_validation__ignore_storage_class";
+        case DisabledValidation::kIgnoreAddressSpace:
+            return "disable_validation__ignore_address_space";
         case DisabledValidation::kEntryPointParameter:
             return "disable_validation__entry_point_parameter";
         case DisabledValidation::kFunctionParameter:
diff --git a/src/tint/ast/disable_validation_attribute.h b/src/tint/ast/disable_validation_attribute.h
index a109d18..d4c7455 100644
--- a/src/tint/ast/disable_validation_attribute.h
+++ b/src/tint/ast/disable_validation_attribute.h
@@ -29,9 +29,9 @@
     /// When applied to a module-scoped variable, the validator will not complain if two resource
     /// variables have the same binding points.
     kBindingPointCollision,
-    /// When applied to a variable, the validator will not complain about the declared storage
-    /// class.
-    kIgnoreStorageClass,
+    /// When applied to a variable, the validator will not complain about the declared address
+    /// space.
+    kIgnoreAddressSpace,
     /// When applied to an entry-point function parameter, the validator will not check for entry IO
     /// attributes.
     kEntryPointParameter,
diff --git a/src/tint/ast/module_test.cc b/src/tint/ast/module_test.cc
index 3093a16..c34805e 100644
--- a/src/tint/ast/module_test.cc
+++ b/src/tint/ast/module_test.cc
@@ -72,7 +72,7 @@
         {
             ProgramBuilder b1;
             ProgramBuilder b2;
-            b1.AST().AddGlobalVariable(b2.Var("var", b2.ty.i32(), ast::StorageClass::kPrivate));
+            b1.AST().AddGlobalVariable(b2.Var("var", b2.ty.i32(), ast::AddressSpace::kPrivate));
         },
         "internal compiler error");
 }
@@ -92,7 +92,7 @@
         ProgramBuilder b;
         b.Func("F", {}, b.ty.void_(), {});
         b.Alias("A", b.ty.u32());
-        b.GlobalVar("V", b.ty.i32(), ast::StorageClass::kPrivate);
+        b.GlobalVar("V", b.ty.i32(), ast::AddressSpace::kPrivate);
         return Program(std::move(b));
     }();
 
diff --git a/src/tint/ast/pointer.cc b/src/tint/ast/pointer.cc
index 796fe85..71b7004 100644
--- a/src/tint/ast/pointer.cc
+++ b/src/tint/ast/pointer.cc
@@ -24,15 +24,15 @@
                  NodeID nid,
                  const Source& src,
                  const Type* const subtype,
-                 ast::StorageClass sc,
+                 ast::AddressSpace addr_space,
                  ast::Access ac)
-    : Base(pid, nid, src), type(subtype), storage_class(sc), access(ac) {}
+    : Base(pid, nid, src), type(subtype), address_space(addr_space), access(ac) {}
 
 std::string Pointer::FriendlyName(const SymbolTable& symbols) const {
     std::ostringstream out;
     out << "ptr<";
-    if (storage_class != ast::StorageClass::kNone) {
-        out << storage_class << ", ";
+    if (address_space != ast::AddressSpace::kNone) {
+        out << address_space << ", ";
     }
     out << type->FriendlyName(symbols);
     if (access != ast::Access::kUndefined) {
@@ -50,7 +50,7 @@
     // Clone arguments outside of create() call to have deterministic ordering
     auto src = ctx->Clone(source);
     auto* ty = ctx->Clone(type);
-    return ctx->dst->create<Pointer>(src, ty, storage_class, access);
+    return ctx->dst->create<Pointer>(src, ty, address_space, access);
 }
 
 }  // namespace tint::ast
diff --git a/src/tint/ast/pointer.h b/src/tint/ast/pointer.h
index 61eff88..0e1e090 100644
--- a/src/tint/ast/pointer.h
+++ b/src/tint/ast/pointer.h
@@ -18,7 +18,7 @@
 #include <string>
 
 #include "src/tint/ast/access.h"
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/ast/type.h"
 
 namespace tint::ast {
@@ -31,13 +31,13 @@
     /// @param nid the unique node identifier
     /// @param src the source of this node
     /// @param subtype the pointee type
-    /// @param storage_class the storage class of the pointer
+    /// @param address_space the address space of the pointer
     /// @param access the access control of the pointer
     Pointer(ProgramID pid,
             NodeID nid,
             const Source& src,
             const Type* const subtype,
-            ast::StorageClass storage_class,
+            ast::AddressSpace address_space,
             ast::Access access);
     /// Move constructor
     Pointer(Pointer&&);
@@ -56,8 +56,8 @@
     /// The pointee type
     const Type* const type;
 
-    /// The storage class of the pointer
-    ast::StorageClass const storage_class;
+    /// The address space of the pointer
+    ast::AddressSpace const address_space;
 
     /// The access control of the pointer
     ast::Access const access;
diff --git a/src/tint/ast/pointer_test.cc b/src/tint/ast/pointer_test.cc
index dbfd3b0..aea260a 100644
--- a/src/tint/ast/pointer_test.cc
+++ b/src/tint/ast/pointer_test.cc
@@ -24,21 +24,21 @@
 
 TEST_F(AstPointerTest, Creation) {
     auto* i32 = create<I32>();
-    auto* p = create<Pointer>(i32, ast::StorageClass::kStorage, Access::kRead);
+    auto* p = create<Pointer>(i32, ast::AddressSpace::kStorage, Access::kRead);
     EXPECT_EQ(p->type, i32);
-    EXPECT_EQ(p->storage_class, ast::StorageClass::kStorage);
+    EXPECT_EQ(p->address_space, ast::AddressSpace::kStorage);
     EXPECT_EQ(p->access, Access::kRead);
 }
 
 TEST_F(AstPointerTest, FriendlyName) {
     auto* i32 = create<I32>();
-    auto* p = create<Pointer>(i32, ast::StorageClass::kWorkgroup, Access::kUndefined);
+    auto* p = create<Pointer>(i32, ast::AddressSpace::kWorkgroup, Access::kUndefined);
     EXPECT_EQ(p->FriendlyName(Symbols()), "ptr<workgroup, i32>");
 }
 
 TEST_F(AstPointerTest, FriendlyNameWithAccess) {
     auto* i32 = create<I32>();
-    auto* p = create<Pointer>(i32, ast::StorageClass::kStorage, Access::kReadWrite);
+    auto* p = create<Pointer>(i32, ast::AddressSpace::kStorage, Access::kReadWrite);
     EXPECT_EQ(p->FriendlyName(Symbols()), "ptr<storage, i32, read_write>");
 }
 
diff --git a/src/tint/ast/storage_class.h.tmpl b/src/tint/ast/storage_class.h.tmpl
deleted file mode 100644
index c21d6f6..0000000
--- a/src/tint/ast/storage_class.h.tmpl
+++ /dev/null
@@ -1,37 +0,0 @@
-{{- /*
---------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate storage_class.h
-
-To update the generated file, run:
-    ./tools/run gen
-
-See:
-* tools/src/cmd/gen for structures used by this template
-* https://golang.org/pkg/text/template/ for documentation on the template syntax
---------------------------------------------------------------------------------
-*/ -}}
-
-{{- Import "src/tint/templates/enums.tmpl.inc" -}}
-{{- $enum := (Sem.Enum "storage_class") -}}
-
-#ifndef SRC_TINT_AST_STORAGE_CLASS_H_
-#define SRC_TINT_AST_STORAGE_CLASS_H_
-
-#include <ostream>
-
-namespace tint::ast {
-
-/// Storage class of a given pointer.
-{{ Eval "DeclareEnum" $enum}}
-
-/// @returns true if the StorageClass is host-shareable
-/// @param sc the StorageClass
-/// @see https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable
-inline bool IsHostShareable(StorageClass sc) {
-    return sc == ast::StorageClass::kUniform || sc == ast::StorageClass::kStorage ||
-           sc == ast::StorageClass::kPushConstant;
-}
-
-}  // namespace tint::ast
-
-#endif  // SRC_TINT_AST_STORAGE_CLASS_H_
diff --git a/src/tint/ast/storage_class_test.cc b/src/tint/ast/storage_class_test.cc
deleted file mode 100644
index 1d12d45..0000000
--- a/src/tint/ast/storage_class_test.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2022 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.
-
-////////////////////////////////////////////////////////////////////////////////
-// File generated by tools/src/cmd/gen
-// using the template:
-//   src/tint/ast/storage_class_test.cc.tmpl
-//
-// Do not modify this file directly
-////////////////////////////////////////////////////////////////////////////////
-
-#include "src/tint/ast/storage_class.h"
-
-#include <string>
-
-#include "src/tint/ast/test_helper.h"
-#include "src/tint/utils/string.h"
-
-namespace tint::ast {
-namespace {
-
-namespace parse_print_tests {
-
-struct Case {
-    const char* string;
-    StorageClass value;
-};
-
-inline std::ostream& operator<<(std::ostream& out, Case c) {
-    return out << "'" << std::string(c.string) << "'";
-}
-
-static constexpr Case kValidCases[] = {
-    {"function", StorageClass::kFunction},   {"private", StorageClass::kPrivate},
-    {"workgroup", StorageClass::kWorkgroup}, {"uniform", StorageClass::kUniform},
-    {"storage", StorageClass::kStorage},     {"push_constant", StorageClass::kPushConstant},
-};
-
-static constexpr Case kInvalidCases[] = {
-    {"fccnctin", StorageClass::kInvalid},       {"ucti3", StorageClass::kInvalid},
-    {"functVon", StorageClass::kInvalid},       {"priv1te", StorageClass::kInvalid},
-    {"pqiJate", StorageClass::kInvalid},        {"privat7ll", StorageClass::kInvalid},
-    {"workroppqHH", StorageClass::kInvalid},    {"workru", StorageClass::kInvalid},
-    {"wbkgGoup", StorageClass::kInvalid},       {"unifiivm", StorageClass::kInvalid},
-    {"8WWiform", StorageClass::kInvalid},       {"uxxform", StorageClass::kInvalid},
-    {"sXraggg", StorageClass::kInvalid},        {"traXe", StorageClass::kInvalid},
-    {"stor3ge", StorageClass::kInvalid},        {"push_constanE", StorageClass::kInvalid},
-    {"push_TTPnstant", StorageClass::kInvalid}, {"puxxdh_constan", StorageClass::kInvalid},
-};
-
-using StorageClassParseTest = testing::TestWithParam<Case>;
-
-TEST_P(StorageClassParseTest, Parse) {
-    const char* string = GetParam().string;
-    StorageClass expect = GetParam().value;
-    EXPECT_EQ(expect, ParseStorageClass(string));
-}
-
-INSTANTIATE_TEST_SUITE_P(ValidCases, StorageClassParseTest, testing::ValuesIn(kValidCases));
-INSTANTIATE_TEST_SUITE_P(InvalidCases, StorageClassParseTest, testing::ValuesIn(kInvalidCases));
-
-using StorageClassPrintTest = testing::TestWithParam<Case>;
-
-TEST_P(StorageClassPrintTest, Print) {
-    StorageClass value = GetParam().value;
-    const char* expect = GetParam().string;
-    EXPECT_EQ(expect, utils::ToString(value));
-}
-
-INSTANTIATE_TEST_SUITE_P(ValidCases, StorageClassPrintTest, testing::ValuesIn(kValidCases));
-
-}  // namespace parse_print_tests
-
-}  // namespace
-}  // namespace tint::ast
diff --git a/src/tint/ast/var.cc b/src/tint/ast/var.cc
index 474cff2..26c23f6 100644
--- a/src/tint/ast/var.cc
+++ b/src/tint/ast/var.cc
@@ -25,12 +25,12 @@
          const Source& src,
          const Symbol& sym,
          const ast::Type* ty,
-         StorageClass storage_class,
+         AddressSpace address_space,
          Access access,
          const Expression* ctor,
          utils::VectorRef<const Attribute*> attrs)
     : Base(pid, nid, src, sym, ty, ctor, std::move(attrs)),
-      declared_storage_class(storage_class),
+      declared_address_space(address_space),
       declared_access(access) {}
 
 Var::Var(Var&&) = default;
@@ -47,7 +47,7 @@
     auto* ty = ctx->Clone(type);
     auto* ctor = ctx->Clone(constructor);
     auto attrs = ctx->Clone(attributes);
-    return ctx->dst->create<Var>(src, sym, ty, declared_storage_class, declared_access, ctor,
+    return ctx->dst->create<Var>(src, sym, ty, declared_address_space, declared_access, ctor,
                                  std::move(attrs));
 }
 
diff --git a/src/tint/ast/var.h b/src/tint/ast/var.h
index 908a9b3..5234165 100644
--- a/src/tint/ast/var.h
+++ b/src/tint/ast/var.h
@@ -28,11 +28,11 @@
 ///
 /// ```
 ///  // Declared outside a function, i.e. at module scope, requires
-///  // a storage class.
+///  // a address space.
 ///  var<workgroup> width : i32;     // no initializer
 ///  var<private> height : i32 = 3;  // with initializer
 ///
-///  // A variable declared inside a function doesn't take a storage class,
+///  // A variable declared inside a function doesn't take a address space,
 ///  // and maps to SPIR-V Function storage.
 ///  var computed_depth : i32;
 ///  var area : i32 = compute_area(width, height);
@@ -47,7 +47,7 @@
     /// @param source the variable source
     /// @param sym the variable symbol
     /// @param type the declared variable type
-    /// @param declared_storage_class the declared storage class
+    /// @param declared_address_space the declared address space
     /// @param declared_access the declared access control
     /// @param constructor the constructor expression
     /// @param attributes the variable attributes
@@ -56,7 +56,7 @@
         const Source& source,
         const Symbol& sym,
         const ast::Type* type,
-        StorageClass declared_storage_class,
+        AddressSpace declared_address_space,
         Access declared_access,
         const Expression* constructor,
         utils::VectorRef<const Attribute*> attributes);
@@ -76,8 +76,8 @@
     /// @return the newly cloned node
     const Var* Clone(CloneContext* ctx) const override;
 
-    /// The declared storage class
-    const StorageClass declared_storage_class;
+    /// The declared address space
+    const AddressSpace declared_address_space;
 
     /// The declared access control
     const Access declared_access;
diff --git a/src/tint/ast/variable.h b/src/tint/ast/variable.h
index 8772f5b..ea0ff99 100644
--- a/src/tint/ast/variable.h
+++ b/src/tint/ast/variable.h
@@ -19,11 +19,11 @@
 #include <vector>
 
 #include "src/tint/ast/access.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/ast/attribute.h"
 #include "src/tint/ast/binding_attribute.h"
 #include "src/tint/ast/expression.h"
 #include "src/tint/ast/group_attribute.h"
-#include "src/tint/ast/storage_class.h"
 
 // Forward declarations
 namespace tint::ast {
diff --git a/src/tint/ast/variable_test.cc b/src/tint/ast/variable_test.cc
index 2fed042..01f3d8e 100644
--- a/src/tint/ast/variable_test.cc
+++ b/src/tint/ast/variable_test.cc
@@ -25,10 +25,10 @@
 using VariableTest = TestHelper;
 
 TEST_F(VariableTest, Creation) {
-    auto* v = Var("my_var", ty.i32(), StorageClass::kFunction);
+    auto* v = Var("my_var", ty.i32(), AddressSpace::kFunction);
 
     EXPECT_EQ(v->symbol, Symbol(1, ID()));
-    EXPECT_EQ(v->declared_storage_class, StorageClass::kFunction);
+    EXPECT_EQ(v->declared_address_space, AddressSpace::kFunction);
     EXPECT_TRUE(v->type->Is<ast::I32>());
     EXPECT_EQ(v->source.range.begin.line, 0u);
     EXPECT_EQ(v->source.range.begin.column, 0u);
@@ -38,10 +38,10 @@
 
 TEST_F(VariableTest, CreationWithSource) {
     auto* v = Var(Source{Source::Range{Source::Location{27, 4}, Source::Location{27, 5}}}, "i",
-                  ty.f32(), StorageClass::kPrivate, utils::Empty);
+                  ty.f32(), AddressSpace::kPrivate, utils::Empty);
 
     EXPECT_EQ(v->symbol, Symbol(1, ID()));
-    EXPECT_EQ(v->declared_storage_class, StorageClass::kPrivate);
+    EXPECT_EQ(v->declared_address_space, AddressSpace::kPrivate);
     EXPECT_TRUE(v->type->Is<ast::F32>());
     EXPECT_EQ(v->source.range.begin.line, 27u);
     EXPECT_EQ(v->source.range.begin.column, 4u);
@@ -51,10 +51,10 @@
 
 TEST_F(VariableTest, CreationEmpty) {
     auto* v = Var(Source{Source::Range{Source::Location{27, 4}, Source::Location{27, 7}}}, "a_var",
-                  ty.i32(), StorageClass::kWorkgroup, utils::Empty);
+                  ty.i32(), AddressSpace::kWorkgroup, utils::Empty);
 
     EXPECT_EQ(v->symbol, Symbol(1, ID()));
-    EXPECT_EQ(v->declared_storage_class, StorageClass::kWorkgroup);
+    EXPECT_EQ(v->declared_address_space, AddressSpace::kWorkgroup);
     EXPECT_TRUE(v->type->Is<ast::I32>());
     EXPECT_EQ(v->source.range.begin.line, 27u);
     EXPECT_EQ(v->source.range.begin.column, 4u);
@@ -92,7 +92,7 @@
 }
 
 TEST_F(VariableTest, WithAttributes) {
-    auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, Location(1_u),
+    auto* var = Var("my_var", ty.i32(), AddressSpace::kFunction, Location(1_u),
                     Builtin(BuiltinValue::kPosition), Id(1200_u));
 
     auto& attributes = var->attributes;
@@ -107,22 +107,22 @@
 }
 
 TEST_F(VariableTest, HasBindingPoint_BothProvided) {
-    auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, Binding(2_a), Group(1_a));
+    auto* var = Var("my_var", ty.i32(), AddressSpace::kFunction, Binding(2_a), Group(1_a));
     EXPECT_TRUE(var->HasBindingPoint());
 }
 
 TEST_F(VariableTest, HasBindingPoint_NeitherProvided) {
-    auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, utils::Empty);
+    auto* var = Var("my_var", ty.i32(), AddressSpace::kFunction, utils::Empty);
     EXPECT_FALSE(var->HasBindingPoint());
 }
 
 TEST_F(VariableTest, HasBindingPoint_MissingGroupAttribute) {
-    auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, Binding(2_a));
+    auto* var = Var("my_var", ty.i32(), AddressSpace::kFunction, Binding(2_a));
     EXPECT_FALSE(var->HasBindingPoint());
 }
 
 TEST_F(VariableTest, HasBindingPoint_MissingBindingAttribute) {
-    auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, Group(1_a));
+    auto* var = Var("my_var", ty.i32(), AddressSpace::kFunction, Group(1_a));
     EXPECT_FALSE(var->HasBindingPoint());
 }
 
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc
index f201047..93466d8 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc
@@ -272,7 +272,7 @@
 
 TEST(ReplaceIdentifierTest, NotApplicable5) {
     // Can't replace `a` with `b` since the latter has a wrong access mode
-    // (`read` for uniform storage class).
+    // (`read` for uniform address space).
     std::string shader = R"(
 struct S {
   a: i32
@@ -343,7 +343,7 @@
 
 TEST(ReplaceIdentifierTest, NotApplicable8) {
     // Can't replace `ptr_b` with `c` since the latter has a wrong access mode and
-    // storage class.
+    // address space.
     std::string shader = R"(
 struct S {
   a: i32
@@ -451,7 +451,7 @@
 }
 
 TEST(ReplaceIdentifierTest, Applicable1) {
-    // Can replace `a` with `b` (same storage class).
+    // Can replace `a` with `b` (same address space).
     std::string shader = R"(
 fn f() {
   var b : vec2<u32>;
@@ -611,7 +611,7 @@
 
 TEST(ReplaceIdentifierTest, NotApplicable14) {
     // Can't replace `ptr_a` with `ptr_b` (both are pointers with different
-    // storage class).
+    // address space).
     std::string shader = R"(
 var<private> b: vec2<u32>;
 fn f() {
diff --git a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
index c5125a4..7d16aad 100644
--- a/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
+++ b/src/tint/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
@@ -416,7 +416,7 @@
         "=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>="};
     std::vector<std::string> expression_operators{"+",  "-",  "*", "/",  "%",  "&&", "||",
                                                   "&",  "|",  "^", "<<", ">>", "<",  ">",
-                                                  "<=", ">=", "!", "!=", "~"};
+                                                  "<=", ">=", "!", "==", "!=", "~"};
     std::vector<std::string> increment_operators{"++", "--"};
     for (auto operators : {assignment_operators, expression_operators, increment_operators}) {
         auto it = std::find(operators.begin(), operators.end(), existing_operator);
@@ -471,6 +471,12 @@
         switch (first_character) {
             case '!':
             case '^':
+            case '*':
+            case '/':
+            case '%':
+            case '=':
+                // The above cases are all stand-alone operators, and if followed by '=' are also
+                // operators.
                 switch (second_character) {
                     case '=':
                         return {{current_index, 2}};
@@ -481,26 +487,15 @@
             case '&':
             case '+':
             case '-':
+                // The above cases are all stand-alone operators, and if repeated or followed by '='
+                // are also operators.
                 if (second_character == first_character || second_character == '=') {
                     return {{current_index, 2}};
                 }
                 return {{current_index, 1}};
-            case '*':
-            case '/':
-            case '%':
-                switch (second_character) {
-                    case '=':
-                        return {{current_index, 2}};
-                    default:
-                        return {{current_index, 1}};
-                }
-            case '=':
-                if (second_character == '=') {
-                    return {{current_index, 2}};
-                }
-                return {{current_index, 1}};
             case '<':
             case '>':
+                // The following caters for '<', '<=', '<<', '<<=', '>', '>=', '>>' and '>>='.
                 if (second_character == '=') {
                     return {{current_index, 2}};
                 }
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index d32ef6f..3520ae3 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -565,7 +565,7 @@
     uint32_t total_size = 0;
     auto* func_sem = program_->Sem().Get(func);
     for (const sem::Variable* var : func_sem->TransitivelyReferencedGlobals()) {
-        if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
+        if (var->AddressSpace() == ast::AddressSpace::kWorkgroup) {
             auto* ty = var->Type()->UnwrapRef();
             uint32_t align = ty->Align();
             uint32_t size = ty->Size();
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index 5a190f6..7e81ba0 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -1811,7 +1811,7 @@
         "foo_type",
         utils::Vector{
             Member("0i32", ty.i32()),
-            Member("b", ty.array(ty.u32(), 4_u, /*stride*/ 16), utils::Vector{MemberAlign(16_u)}),
+            Member("b", ty.array(ty.u32(), 4_u, /*stride*/ 16), utils::Vector{MemberAlign(16_i)}),
         });
 
     AddUniformBuffer("foo_ub", ty.Of(foo_struct_type), 0, 0);
@@ -2655,48 +2655,6 @@
     EXPECT_EQ(0u, result.size());
 }
 
-TEST_P(InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam, DISABLED_textureSample) {
-    auto* multisampled_texture_type =
-        ty.multisampled_texture(GetParam().type_dim, GetBaseType(GetParam().sampled_kind));
-    AddResource("foo_texture", multisampled_texture_type, 0, 0);
-    AddSampler("foo_sampler", 0, 1);
-    auto* coord_type = GetCoordsType(GetParam().type_dim, ty.f32());
-    AddGlobalVariable("foo_coords", coord_type);
-    AddGlobalVariable("foo_array_index", ty.i32());
-
-    MakeSamplerReferenceBodyFunction("ep", "foo_texture", "foo_sampler", "foo_coords",
-                                     "foo_array_index", GetBaseType(GetParam().sampled_kind),
-                                     utils::Vector{
-                                         Stage(ast::PipelineStage::kFragment),
-                                     });
-
-    Inspector& inspector = Build();
-
-    auto result = inspector.GetMultisampledTextureResourceBindings("ep");
-    ASSERT_FALSE(inspector.has_error()) << inspector.error();
-    ASSERT_EQ(1u, result.size());
-
-    EXPECT_EQ(ResourceBinding::ResourceType::kMultisampledTexture, result[0].resource_type);
-    EXPECT_EQ(0u, result[0].bind_group);
-    EXPECT_EQ(0u, result[0].binding);
-    EXPECT_EQ(GetParam().inspector_dim, result[0].dim);
-    EXPECT_EQ(GetParam().sampled_kind, result[0].sampled_kind);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    InspectorGetMultisampledArrayTextureResourceBindingsTest,
-    InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam,
-    testing::Values(
-        GetMultisampledTextureTestParams{ast::TextureDimension::k2dArray,
-                                         inspector::ResourceBinding::TextureDimension::k2dArray,
-                                         inspector::ResourceBinding::SampledKind::kFloat},
-        GetMultisampledTextureTestParams{ast::TextureDimension::k2dArray,
-                                         inspector::ResourceBinding::TextureDimension::k2dArray,
-                                         inspector::ResourceBinding::SampledKind::kSInt},
-        GetMultisampledTextureTestParams{ast::TextureDimension::k2dArray,
-                                         inspector::ResourceBinding::TextureDimension::k2dArray,
-                                         inspector::ResourceBinding::SampledKind::kUInt}));
-
 TEST_F(InspectorGetStorageTextureResourceBindingsTest, Empty) {
     MakeEmptyBodyFunction("ep", utils::Vector{
                                     Stage(ast::PipelineStage::kFragment),
@@ -3239,7 +3197,7 @@
     // here the struct is expected to occupy 1024 bytes of workgroup storage.
     const auto* wg_struct_type = MakeStructTypeFromMembers(
         "WgStruct", utils::Vector{
-                        MakeStructMember(0, ty.f32(), utils::Vector{MemberAlign(1024_u)}),
+                        MakeStructMember(0, ty.f32(), utils::Vector{MemberAlign(1024_i)}),
                     });
 
     AddWorkgroupStorage("wg_struct_var", ty.Of(wg_struct_type));
diff --git a/src/tint/inspector/test_inspector_builder.cc b/src/tint/inspector/test_inspector_builder.cc
index ce341a6..79122dc 100644
--- a/src/tint/inspector/test_inspector_builder.cc
+++ b/src/tint/inspector/test_inspector_builder.cc
@@ -126,11 +126,11 @@
                                         const ast::Type* type,
                                         uint32_t group,
                                         uint32_t binding) {
-    GlobalVar(name, type, ast::StorageClass::kUniform, Binding(AInt(binding)), Group(AInt(group)));
+    GlobalVar(name, type, ast::AddressSpace::kUniform, Binding(AInt(binding)), Group(AInt(group)));
 }
 
 void InspectorBuilder::AddWorkgroupStorage(const std::string& name, const ast::Type* type) {
-    GlobalVar(name, type, ast::StorageClass::kWorkgroup);
+    GlobalVar(name, type, ast::AddressSpace::kWorkgroup);
 }
 
 void InspectorBuilder::AddStorageBuffer(const std::string& name,
@@ -138,7 +138,7 @@
                                         ast::Access access,
                                         uint32_t group,
                                         uint32_t binding) {
-    GlobalVar(name, type, ast::StorageClass::kStorage, access, Binding(AInt(binding)),
+    GlobalVar(name, type, ast::AddressSpace::kStorage, access, Binding(AInt(binding)),
               Group(AInt(group)));
 }
 
@@ -188,7 +188,7 @@
 }
 
 void InspectorBuilder::AddGlobalVariable(const std::string& name, const ast::Type* type) {
-    GlobalVar(name, type, ast::StorageClass::kPrivate);
+    GlobalVar(name, type, ast::AddressSpace::kPrivate);
 }
 
 const ast::Function* InspectorBuilder::MakeSamplerReferenceBodyFunction(
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index b88ff59..debfa0e 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -54,7 +54,7 @@
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
-enum storage_class {
+enum address_space {
   @internal none
   function
   private
@@ -125,7 +125,7 @@
 type mat4x4<T>
 @display("vec{N}<{T}>")     type vec<N: num, T>
 @display("mat{N}x{M}<{T}>") type mat<N: num, M: num, T>
-type ptr<S: storage_class, T, A: access>
+type ptr<S: address_space, T, A: access>
 type atomic<T>
 type array<T>
 type sampler
@@ -214,14 +214,14 @@
 match read_write: access.read_write
 
 match function_private_workgroup
-  : storage_class.function
-  | storage_class.private
-  | storage_class.workgroup
+  : address_space.function
+  | address_space.private
+  | address_space.workgroup
 match workgroup_or_storage
-  : storage_class.workgroup
-  | storage_class.storage
+  : address_space.workgroup
+  | address_space.storage
 match storage
-  : storage_class.storage
+  : address_space.storage
 
 ////////////////////////////////////////////////////////////////////////////////
 // Builtin Functions                                                          //
@@ -510,9 +510,9 @@
 fn round<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn saturate<T: f32_f16>(T) -> T
 fn saturate<T: f32_f16, N: num>(vec<N, T>) -> vec<N, T>
-fn select<T: scalar>(T, T, bool) -> T
-fn select<T: scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
-fn select<N: num, T: scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
+@const("select_bool") fn select<T: abstract_or_scalar>(T, T, bool) -> T
+@const("select_bool") fn select<T: abstract_or_scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
+@const("select_boolvec") fn select<N: num, T: abstract_or_scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
 fn sign<T: f32_f16>(T) -> T
 fn sign<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
 fn sin<T: f32_f16>(T) -> T
diff --git a/src/tint/number.h b/src/tint/number.h
index 69e5983..821e486 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -73,9 +73,9 @@
 using UnwrapNumber = typename detail::NumberUnwrapper<T>::type;
 
 /// Evaluates to true iff T or Number<T> is a floating-point type or is NumberKindF16.
-template <typename T, typename U = std::conditional_t<IsNumber<T>, UnwrapNumber<T>, T>>
-constexpr bool IsFloatingPoint =
-    std::is_floating_point_v<U> || std::is_same_v<T, detail::NumberKindF16>;
+template <typename T>
+constexpr bool IsFloatingPoint = std::is_floating_point_v<UnwrapNumber<T>> ||
+                                 std::is_same_v<UnwrapNumber<T>, detail::NumberKindF16>;
 
 /// Evaluates to true iff T or Number<T> is an integral type.
 template <typename T>
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index e8296a5..116660f 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -172,14 +172,14 @@
         ~VarOptions();
 
         const ast::Type* type = nullptr;
-        ast::StorageClass storage = ast::StorageClass::kNone;
+        ast::AddressSpace address_space = ast::AddressSpace::kNone;
         ast::Access access = ast::Access::kUndefined;
         const ast::Expression* constructor = nullptr;
         utils::Vector<const ast::Attribute*, 4> attributes;
 
       private:
         void Set(const ast::Type* t) { type = t; }
-        void Set(ast::StorageClass sc) { storage = sc; }
+        void Set(ast::AddressSpace addr_space) { address_space = addr_space; }
         void Set(ast::Access ac) { access = ac; }
         void Set(const ast::Expression* c) { constructor = c; }
         void Set(utils::VectorRef<const ast::Attribute*> l) { attributes = std::move(l); }
@@ -902,45 +902,45 @@
         }
 
         /// @param type the type of the pointer
-        /// @param storage_class the storage class of the pointer
+        /// @param address_space the address space of the pointer
         /// @param access the optional access control of the pointer
-        /// @return the pointer to `type` with the given ast::StorageClass
+        /// @return the pointer to `type` with the given ast::AddressSpace
         const ast::Pointer* pointer(const ast::Type* type,
-                                    ast::StorageClass storage_class,
+                                    ast::AddressSpace address_space,
                                     ast::Access access = ast::Access::kUndefined) const {
-            return builder->create<ast::Pointer>(type, storage_class, access);
+            return builder->create<ast::Pointer>(type, address_space, access);
         }
 
         /// @param source the Source of the node
         /// @param type the type of the pointer
-        /// @param storage_class the storage class of the pointer
+        /// @param address_space the address space of the pointer
         /// @param access the optional access control of the pointer
-        /// @return the pointer to `type` with the given ast::StorageClass
+        /// @return the pointer to `type` with the given ast::AddressSpace
         const ast::Pointer* pointer(const Source& source,
                                     const ast::Type* type,
-                                    ast::StorageClass storage_class,
+                                    ast::AddressSpace address_space,
                                     ast::Access access = ast::Access::kUndefined) const {
-            return builder->create<ast::Pointer>(source, type, storage_class, access);
+            return builder->create<ast::Pointer>(source, type, address_space, access);
         }
 
-        /// @param storage_class the storage class of the pointer
+        /// @param address_space the address space of the pointer
         /// @param access the optional access control of the pointer
-        /// @return the pointer to type `T` with the given ast::StorageClass.
+        /// @return the pointer to type `T` with the given ast::AddressSpace.
         template <typename T>
-        const ast::Pointer* pointer(ast::StorageClass storage_class,
+        const ast::Pointer* pointer(ast::AddressSpace address_space,
                                     ast::Access access = ast::Access::kUndefined) const {
-            return pointer(Of<T>(), storage_class, access);
+            return pointer(Of<T>(), address_space, access);
         }
 
         /// @param source the Source of the node
-        /// @param storage_class the storage class of the pointer
+        /// @param address_space the address space of the pointer
         /// @param access the optional access control of the pointer
-        /// @return the pointer to type `T` with the given ast::StorageClass.
+        /// @return the pointer to type `T` with the given ast::AddressSpace.
         template <typename T>
         const ast::Pointer* pointer(const Source& source,
-                                    ast::StorageClass storage_class,
+                                    ast::AddressSpace address_space,
                                     ast::Access access = ast::Access::kUndefined) const {
-            return pointer(source, Of<T>(), storage_class, access);
+            return pointer(source, Of<T>(), address_space, access);
         }
 
         /// @param source the Source of the node
@@ -1651,7 +1651,7 @@
     /// @param options the extra options passed to the ast::Var constructor
     /// Can be any of the following, in any order:
     ///   * ast::Type*          - specifies the variable type
-    ///   * ast::StorageClass   - specifies the variable storage class
+    ///   * ast::AddressSpace   - specifies the variable address space
     ///   * ast::Access         - specifies the variable's access control
     ///   * ast::Expression*    - specifies the variable's initializer expression
     ///   * ast::Attribute*     - specifies the variable's attributes (repeatable, or vector)
@@ -1661,8 +1661,8 @@
     template <typename NAME, typename... OPTIONS, typename = DisableIfSource<NAME>>
     const ast::Var* Var(NAME&& name, OPTIONS&&... options) {
         VarOptions opts(std::forward<OPTIONS>(options)...);
-        return create<ast::Var>(Sym(std::forward<NAME>(name)), opts.type, opts.storage, opts.access,
-                                opts.constructor, std::move(opts.attributes));
+        return create<ast::Var>(Sym(std::forward<NAME>(name)), opts.type, opts.address_space,
+                                opts.access, opts.constructor, std::move(opts.attributes));
     }
 
     /// @param source the variable source
@@ -1670,17 +1670,18 @@
     /// @param options the extra options passed to the ast::Var constructor
     /// Can be any of the following, in any order:
     ///   * ast::Type*          - specifies the variable type
-    ///   * ast::StorageClass   - specifies the variable storage class
+    ///   * ast::AddressSpace   - specifies the variable address space
     ///   * ast::Access         - specifies the variable's access control
     ///   * ast::Expression*    - specifies the variable's initializer expression
     ///   * ast::Attribute*     - specifies the variable's attributes (repeatable, or vector)
     /// Note that non-repeatable arguments of the same type will use the last argument's value.
-    /// @returns a `ast::Var` with the given name, storage and type
+    /// @returns a `ast::Var` with the given name, address_space and type
     template <typename NAME, typename... OPTIONS>
     const ast::Var* Var(const Source& source, NAME&& name, OPTIONS&&... options) {
         VarOptions opts(std::forward<OPTIONS>(options)...);
-        return create<ast::Var>(source, Sym(std::forward<NAME>(name)), opts.type, opts.storage,
-                                opts.access, opts.constructor, std::move(opts.attributes));
+        return create<ast::Var>(source, Sym(std::forward<NAME>(name)), opts.type,
+                                opts.address_space, opts.access, opts.constructor,
+                                std::move(opts.attributes));
     }
 
     /// @param name the variable name
@@ -1773,7 +1774,7 @@
     /// @param options the extra options passed to the ast::Var constructor
     /// Can be any of the following, in any order:
     ///   * ast::Type*          - specifies the variable type
-    ///   * ast::StorageClass   - specifies the variable storage class
+    ///   * ast::AddressSpace   - specifies the variable address space
     ///   * ast::Access         - specifies the variable's access control
     ///   * ast::Expression*    - specifies the variable's initializer expression
     ///   * ast::Attribute*     - specifies the variable's attributes (repeatable, or vector)
@@ -1792,7 +1793,7 @@
     /// @param options the extra options passed to the ast::Var constructor
     /// Can be any of the following, in any order:
     ///   * ast::Type*          - specifies the variable type
-    ///   * ast::StorageClass   - specifies the variable storage class
+    ///   * ast::AddressSpace   - specifies the variable address space
     ///   * ast::Access         - specifies the variable's access control
     ///   * ast::Expression*    - specifies the variable's initializer expression
     ///   * ast::Attribute*    - specifies the variable's attributes (repeatable, or vector)
diff --git a/src/tint/program_test.cc b/src/tint/program_test.cc
index 39d7c07..d2f1673 100644
--- a/src/tint/program_test.cc
+++ b/src/tint/program_test.cc
@@ -46,7 +46,7 @@
 }
 
 TEST_F(ProgramTest, Assert_GlobalVariable) {
-    GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.f32(), ast::AddressSpace::kPrivate);
 
     Program program(std::move(*this));
     EXPECT_TRUE(program.IsValid());
diff --git a/src/tint/reader/spirv/enum_converter.cc b/src/tint/reader/spirv/enum_converter.cc
index 8791670..bc899d7 100644
--- a/src/tint/reader/spirv/enum_converter.cc
+++ b/src/tint/reader/spirv/enum_converter.cc
@@ -36,30 +36,30 @@
     return ast::PipelineStage::kNone;
 }
 
-ast::StorageClass EnumConverter::ToStorageClass(const SpvStorageClass sc) {
+ast::AddressSpace EnumConverter::ToAddressSpace(const SpvStorageClass sc) {
     switch (sc) {
         case SpvStorageClassInput:
-            return ast::StorageClass::kIn;
+            return ast::AddressSpace::kIn;
         case SpvStorageClassOutput:
-            return ast::StorageClass::kOut;
+            return ast::AddressSpace::kOut;
         case SpvStorageClassUniform:
-            return ast::StorageClass::kUniform;
+            return ast::AddressSpace::kUniform;
         case SpvStorageClassWorkgroup:
-            return ast::StorageClass::kWorkgroup;
+            return ast::AddressSpace::kWorkgroup;
         case SpvStorageClassUniformConstant:
-            return ast::StorageClass::kNone;
+            return ast::AddressSpace::kNone;
         case SpvStorageClassStorageBuffer:
-            return ast::StorageClass::kStorage;
+            return ast::AddressSpace::kStorage;
         case SpvStorageClassPrivate:
-            return ast::StorageClass::kPrivate;
+            return ast::AddressSpace::kPrivate;
         case SpvStorageClassFunction:
-            return ast::StorageClass::kFunction;
+            return ast::AddressSpace::kFunction;
         default:
             break;
     }
 
     Fail() << "unknown SPIR-V storage class: " << uint32_t(sc);
-    return ast::StorageClass::kInvalid;
+    return ast::AddressSpace::kInvalid;
 }
 
 ast::BuiltinValue EnumConverter::ToBuiltin(SpvBuiltIn b) {
diff --git a/src/tint/reader/spirv/enum_converter.h b/src/tint/reader/spirv/enum_converter.h
index fc3ad83..1ebea36 100644
--- a/src/tint/reader/spirv/enum_converter.h
+++ b/src/tint/reader/spirv/enum_converter.h
@@ -16,9 +16,9 @@
 #define SRC_TINT_READER_SPIRV_ENUM_CONVERTER_H_
 
 #include "spirv/unified1/spirv.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/ast/builtin_value.h"
 #include "src/tint/ast/pipeline_stage.h"
-#include "src/tint/ast/storage_class.h"
 #include "src/tint/reader/spirv/fail_stream.h"
 #include "src/tint/sem/storage_texture.h"
 
@@ -39,11 +39,11 @@
     /// @returns a Tint AST pipeline stage
     ast::PipelineStage ToPipelineStage(SpvExecutionModel model);
 
-    /// Converts a SPIR-V storage class to a Tint storage class.
+    /// Converts a SPIR-V storage class to a Tint address space.
     /// On failure, logs an error and returns kNone
     /// @param sc the SPIR-V storage class
-    /// @returns a Tint AST storage class
-    ast::StorageClass ToStorageClass(const SpvStorageClass sc);
+    /// @returns a Tint AST address space
+    ast::AddressSpace ToAddressSpace(const SpvStorageClass sc);
 
     /// Converts a SPIR-V Builtin value a Tint Builtin.
     /// On failure, logs an error and returns kNone
diff --git a/src/tint/reader/spirv/enum_converter_test.cc b/src/tint/reader/spirv/enum_converter_test.cc
index 95a6264..623f6ea 100644
--- a/src/tint/reader/spirv/enum_converter_test.cc
+++ b/src/tint/reader/spirv/enum_converter_test.cc
@@ -84,7 +84,7 @@
 struct StorageClassCase {
     SpvStorageClass sc;
     bool expect_success;
-    ast::StorageClass expected;
+    ast::AddressSpace expected;
 };
 inline std::ostream& operator<<(std::ostream& out, StorageClassCase scc) {
     out << "StorageClassCase{ SpvStorageClass:" << int(scc.sc)
@@ -110,7 +110,7 @@
 TEST_P(SpvStorageClassTest, Samples) {
     const auto params = GetParam();
 
-    const auto result = converter_.ToStorageClass(params.sc);
+    const auto result = converter_.ToAddressSpace(params.sc);
     EXPECT_EQ(success_, params.expect_success);
     if (params.expect_success) {
         EXPECT_EQ(result, params.expected);
@@ -125,19 +125,19 @@
     EnumConverterGood,
     SpvStorageClassTest,
     testing::Values(
-        StorageClassCase{SpvStorageClassInput, true, ast::StorageClass::kIn},
-        StorageClassCase{SpvStorageClassOutput, true, ast::StorageClass::kOut},
-        StorageClassCase{SpvStorageClassUniform, true, ast::StorageClass::kUniform},
-        StorageClassCase{SpvStorageClassWorkgroup, true, ast::StorageClass::kWorkgroup},
-        StorageClassCase{SpvStorageClassUniformConstant, true, ast::StorageClass::kNone},
-        StorageClassCase{SpvStorageClassStorageBuffer, true, ast::StorageClass::kStorage},
-        StorageClassCase{SpvStorageClassPrivate, true, ast::StorageClass::kPrivate},
-        StorageClassCase{SpvStorageClassFunction, true, ast::StorageClass::kFunction}));
+        StorageClassCase{SpvStorageClassInput, true, ast::AddressSpace::kIn},
+        StorageClassCase{SpvStorageClassOutput, true, ast::AddressSpace::kOut},
+        StorageClassCase{SpvStorageClassUniform, true, ast::AddressSpace::kUniform},
+        StorageClassCase{SpvStorageClassWorkgroup, true, ast::AddressSpace::kWorkgroup},
+        StorageClassCase{SpvStorageClassUniformConstant, true, ast::AddressSpace::kNone},
+        StorageClassCase{SpvStorageClassStorageBuffer, true, ast::AddressSpace::kStorage},
+        StorageClassCase{SpvStorageClassPrivate, true, ast::AddressSpace::kPrivate},
+        StorageClassCase{SpvStorageClassFunction, true, ast::AddressSpace::kFunction}));
 
 INSTANTIATE_TEST_SUITE_P(EnumConverterBad,
                          SpvStorageClassTest,
                          testing::Values(StorageClassCase{static_cast<SpvStorageClass>(9999), false,
-                                                          ast::StorageClass::kInvalid}));
+                                                          ast::AddressSpace::kInvalid}));
 
 // Builtin
 
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 00b49d9..d00dad0 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -2500,11 +2500,11 @@
                 return false;
             }
         }
-        auto* var = parser_impl_.MakeVar(inst.result_id(), ast::StorageClass::kNone, var_store_type,
+        auto* var = parser_impl_.MakeVar(inst.result_id(), ast::AddressSpace::kNone, var_store_type,
                                          constructor, AttributeList{});
         auto* var_decl_stmt = create<ast::VariableDeclStatement>(Source{}, var);
         AddStatement(var_decl_stmt);
-        auto* var_type = ty_.Reference(var_store_type, ast::StorageClass::kNone);
+        auto* var_type = ty_.Reference(var_store_type, ast::AddressSpace::kNone);
         identifier_types_.emplace(inst.result_id(), var_type);
     }
     return success();
@@ -2679,7 +2679,7 @@
     // - A kContinue can contain a kContinue
     //   This is possible in Vulkan SPIR-V, but Tint disallows this by the rule
     //   that a block can be continue target for at most one header block. See
-    //   test DISABLED_BlockIsContinueForMoreThanOneHeader. If we generalize this,
+    //   test BlockIsContinueForMoreThanOneHeader. If we generalize this,
     //   then by a dominance argument, the inner loop continue target can only be
     //   a single-block loop.
     // TODO(dneto): Handle this case.
@@ -3356,11 +3356,11 @@
     for (auto id : sorted_by_index(block_info.hoisted_ids)) {
         const auto* def_inst = def_use_mgr_->GetDef(id);
         TINT_ASSERT(Reader, def_inst);
-        auto* storage_type = RemapStorageClass(parser_impl_.ConvertType(def_inst->type_id()), id);
+        auto* storage_type = RemapAddressSpace(parser_impl_.ConvertType(def_inst->type_id()), id);
         AddStatement(create<ast::VariableDeclStatement>(
-            Source{}, parser_impl_.MakeVar(id, ast::StorageClass::kNone, storage_type, nullptr,
+            Source{}, parser_impl_.MakeVar(id, ast::AddressSpace::kNone, storage_type, nullptr,
                                            AttributeList{})));
-        auto* type = ty_.Reference(storage_type, ast::StorageClass::kNone);
+        auto* type = ty_.Reference(storage_type, ast::AddressSpace::kNone);
         identifier_types_.emplace(id, type);
     }
 
@@ -3720,7 +3720,7 @@
             if (!expr) {
                 return false;
             }
-            expr.type = RemapStorageClass(expr.type, result_id);
+            expr.type = RemapAddressSpace(expr.type, result_id);
             return EmitConstDefOrWriteToHoistedVar(inst, expr);
         }
 
@@ -3777,15 +3777,15 @@
     return parser_impl_.RectifyOperandSignedness(inst, std::move(expr));
 }
 
-TypedExpression FunctionEmitter::InferFunctionStorageClass(TypedExpression expr) {
+TypedExpression FunctionEmitter::InferFunctionAddressSpace(TypedExpression expr) {
     TypedExpression result(expr);
     if (const auto* ref = expr.type->UnwrapAlias()->As<Reference>()) {
-        if (ref->storage_class == ast::StorageClass::kNone) {
-            expr.type = ty_.Reference(ref->type, ast::StorageClass::kFunction);
+        if (ref->address_space == ast::AddressSpace::kNone) {
+            expr.type = ty_.Reference(ref->type, ast::AddressSpace::kFunction);
         }
     } else if (const auto* ptr = expr.type->UnwrapAlias()->As<Pointer>()) {
-        if (ptr->storage_class == ast::StorageClass::kNone) {
-            expr.type = ty_.Pointer(ptr->type, ast::StorageClass::kFunction);
+        if (ptr->address_space == ast::AddressSpace::kNone) {
+            expr.type = ty_.Pointer(ptr->type, ast::AddressSpace::kFunction);
         }
     }
     return expr;
@@ -4418,7 +4418,7 @@
     // ever-deeper nested indexing expressions. Start off with an expression
     // for the base, and then bury that inside nested indexing expressions.
     if (!current_expr) {
-        current_expr = InferFunctionStorageClass(MakeOperand(inst, 0));
+        current_expr = InferFunctionAddressSpace(MakeOperand(inst, 0));
         if (current_expr.type->Is<Pointer>()) {
             current_expr = Dereference(current_expr);
         }
@@ -4430,7 +4430,7 @@
         Fail() << "Access chain %" << inst.result_id() << " base pointer is not of pointer type";
         return {};
     }
-    SpvStorageClass storage_class =
+    SpvStorageClass address_space =
         static_cast<SpvStorageClass>(ptr_type_inst->GetSingleWordInOperand(0));
     uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
 
@@ -4521,7 +4521,7 @@
                        << ": " << pointee_type_inst->PrettyPrint();
                 return {};
         }
-        const auto pointer_type_id = type_mgr_->FindPointerToType(pointee_type_id, storage_class);
+        const auto pointer_type_id = type_mgr_->FindPointerToType(pointee_type_id, address_space);
         auto* type = parser_impl_.ConvertType(pointer_type_id, PtrAs::Ref);
         TINT_ASSERT(Reader, type && type->Is<Reference>());
         current_expr = TypedExpression{type, next_expr};
@@ -4799,8 +4799,8 @@
             ++index;
             auto& info = def_info_[result_id];
 
-            // Determine storage class for pointer values. Do this in order because
-            // we might rely on the storage class for a previously-visited definition.
+            // Determine address space for pointer values. Do this in order because
+            // we might rely on the address space for a previously-visited definition.
             // Logical pointers can't be transmitted through OpPhi, so remaining
             // pointer definitions are SSA values, and their definitions must be
             // visited before their uses.
@@ -4809,7 +4809,7 @@
                 if (type->AsPointer()) {
                     if (auto* ast_type = parser_impl_.ConvertType(inst.type_id())) {
                         if (auto* ptr = ast_type->As<Pointer>()) {
-                            info->storage_class = ptr->storage_class;
+                            info->address_space = ptr->address_space;
                         }
                     }
                     switch (inst.opcode()) {
@@ -4823,8 +4823,8 @@
                         case SpvOpCopyObject:
                             // Inherit from the first operand. We need this so we can pick up
                             // a remapped storage buffer.
-                            info->storage_class =
-                                GetStorageClassForPointerValue(inst.GetSingleWordInOperand(0));
+                            info->address_space =
+                                GetAddressSpaceForPointerValue(inst.GetSingleWordInOperand(0));
                             break;
                         default:
                             return Fail() << "pointer defined in function from unknown opcode: "
@@ -4846,11 +4846,11 @@
     return true;
 }
 
-ast::StorageClass FunctionEmitter::GetStorageClassForPointerValue(uint32_t id) {
+ast::AddressSpace FunctionEmitter::GetAddressSpaceForPointerValue(uint32_t id) {
     auto where = def_info_.find(id);
     if (where != def_info_.end()) {
-        auto candidate = where->second.get()->storage_class;
-        if (candidate != ast::StorageClass::kInvalid) {
+        auto candidate = where->second.get()->address_space;
+        if (candidate != ast::AddressSpace::kInvalid) {
             return candidate;
         }
     }
@@ -4858,19 +4858,19 @@
     if (type_id) {
         auto* ast_type = parser_impl_.ConvertType(type_id);
         if (auto* ptr = As<Pointer>(ast_type)) {
-            return ptr->storage_class;
+            return ptr->address_space;
         }
     }
-    return ast::StorageClass::kInvalid;
+    return ast::AddressSpace::kInvalid;
 }
 
-const Type* FunctionEmitter::RemapStorageClass(const Type* type, uint32_t result_id) {
+const Type* FunctionEmitter::RemapAddressSpace(const Type* type, uint32_t result_id) {
     if (auto* ast_ptr_type = As<Pointer>(type)) {
         // Remap an old-style storage buffer pointer to a new-style storage
         // buffer pointer.
-        const auto sc = GetStorageClassForPointerValue(result_id);
-        if (ast_ptr_type->storage_class != sc) {
-            return ty_.Pointer(ast_ptr_type->type, sc);
+        const auto addr_space = GetAddressSpaceForPointerValue(result_id);
+        if (ast_ptr_type->address_space != addr_space) {
+            return ty_.Pointer(ast_ptr_type->type, addr_space);
         }
     }
     return type;
@@ -5053,7 +5053,7 @@
             // Avoid moving combinatorial values across constructs.  This is a
             // simple heuristic to avoid changing the cost of an operation
             // by moving it into or out of a loop, for example.
-            if ((def_info->storage_class == ast::StorageClass::kInvalid) &&
+            if ((def_info->address_space == ast::AddressSpace::kInvalid) &&
                 local_def.used_in_another_construct) {
                 should_hoist_to_let = true;
             }
@@ -6170,7 +6170,7 @@
         // API in parser_impl_.
         var_name = namer_.MakeDerivedName(original_value_name);
 
-        auto* temp_var = builder_.Var(var_name, type->Build(builder_), ast::StorageClass::kNone,
+        auto* temp_var = builder_.Var(var_name, type->Build(builder_), ast::AddressSpace::kNone,
                                       src_vector.expr);
 
         AddStatement(builder_.Decl({}, temp_var));
@@ -6240,7 +6240,7 @@
         // It doesn't correspond to a SPIR-V ID, so we don't use the ordinary
         // API in parser_impl_.
         var_name = namer_.MakeDerivedName(original_value_name);
-        auto* temp_var = builder_.Var(var_name, type->Build(builder_), ast::StorageClass::kNone,
+        auto* temp_var = builder_.Var(var_name, type->Build(builder_), ast::AddressSpace::kNone,
                                       src_composite.expr);
         AddStatement(builder_.Decl({}, temp_var));
     }
@@ -6271,7 +6271,7 @@
         return {};
     }
     return {
-        ty_.Pointer(ref->type, ref->storage_class),
+        ty_.Pointer(ref->type, ref->address_space),
         create<ast::UnaryOpExpression>(Source{}, ast::UnaryOp::kAddressOf, expr.expr),
     };
 }
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index 53c983c..3295551 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -325,12 +325,12 @@
     /// example, pointers. crbug.com/tint/98
     bool requires_hoisted_var_def = false;
 
-    /// The storage class to use for this value, if it is of pointer type.
-    /// This is required to carry a storage class override from a storage
-    /// buffer expressed in the old style (with Uniform storage class)
-    /// that needs to be remapped to StorageBuffer storage class.
+    /// The address space to use for this value, if it is of pointer type.
+    /// This is required to carry an address space override from a storage
+    /// buffer expressed in the old style (with Uniform address space)
+    /// that needs to be remapped to StorageBuffer address space.
     /// This is kInvalid for non-pointers.
-    ast::StorageClass storage_class = ast::StorageClass::kInvalid;
+    ast::AddressSpace address_space = ast::AddressSpace::kInvalid;
 
     /// The expression to use when sinking pointers into their use.
     /// When encountering a use of this instruction, we will emit this expression
@@ -360,8 +360,8 @@
     }
     o << " requires_named_let_def: " << (di.requires_named_let_def ? "true" : "false")
       << " requires_hoisted_var_def: " << (di.requires_hoisted_var_def ? "true" : "false");
-    if (di.storage_class != ast::StorageClass::kNone) {
-        o << " sc:" << int(di.storage_class);
+    if (di.address_space != ast::AddressSpace::kNone) {
+        o << " sc:" << int(di.address_space);
     }
     switch (di.skip) {
         case SkipReason::kDontSkip:
@@ -470,7 +470,7 @@
     /// by the `index_prefix`, which successively indexes into the variable.
     /// Also generates the assignment statements that copy the input parameter
     /// to the corresponding part of the variable.  Assumes the variable
-    /// has already been created in the Private storage class.
+    /// has already been created in the Private address space.
     /// @param var_name The name of the variable
     /// @param var_type The store type of the variable
     /// @param decos The variable's decorations
@@ -496,8 +496,7 @@
     /// expressions that compute the value they contribute to the entry point
     /// return value.  The part of the output variable is specfied
     /// by the `index_prefix`, which successively indexes into the variable.
-    /// Assumes the variable has already been created in the Private storage
-    /// class.
+    /// Assumes the variable has already been created in the Private address space
     /// @param var_name The name of the variable
     /// @param var_type The store type of the variable
     /// @param decos The variable's decorations
@@ -612,19 +611,19 @@
     /// @returns false on failure
     bool RegisterLocallyDefinedValues();
 
-    /// Returns the Tint storage class for the given SPIR-V ID that is a
+    /// Returns the Tint address space for the given SPIR-V ID that is a
     /// pointer value.
     /// @param id a SPIR-V ID for a pointer value
-    /// @returns the storage class
-    ast::StorageClass GetStorageClassForPointerValue(uint32_t id);
+    /// @returns the address space
+    ast::AddressSpace GetAddressSpaceForPointerValue(uint32_t id);
 
-    /// Remaps the storage class for the type of a locally-defined value,
-    /// if necessary. If it's not a pointer type, or if its storage class
+    /// Remaps the address space for the type of a locally-defined value,
+    /// if necessary. If it's not a pointer type, or if its address space
     /// already matches, then the result is a copy of the `type` argument.
     /// @param type the AST type
     /// @param result_id the SPIR-V ID for the locally defined value
     /// @returns an possibly updated type
-    const Type* RemapStorageClass(const Type* type, uint32_t result_id);
+    const Type* RemapAddressSpace(const Type* type, uint32_t result_id);
 
     /// Marks locally defined values when they should get a 'let'
     /// definition in WGSL, or a 'var' definition at an outer scope.
@@ -1005,11 +1004,11 @@
     TypedExpression MakeOperand(const spvtools::opt::Instruction& inst, uint32_t operand_index);
 
     /// Copies a typed expression to the result, but when the type is a pointer
-    /// or reference type, ensures the storage class is not defaulted.  That is,
-    /// it changes a storage class of "none" to "function".
+    /// or reference type, ensures the address space is not defaulted.  That is,
+    /// it changes a address space of "none" to "function".
     /// @param expr a typed expression
     /// @results a copy of the expression, with possibly updated type
-    TypedExpression InferFunctionStorageClass(TypedExpression expr);
+    TypedExpression InferFunctionAddressSpace(TypedExpression expr);
 
     /// Returns an expression for a SPIR-V OpFMod instruction.
     /// @param inst the SPIR-V instruction
diff --git a/src/tint/reader/spirv/function_cfg_test.cc b/src/tint/reader/spirv/function_cfg_test.cc
index fb5aff7..8356e37 100644
--- a/src/tint/reader/spirv/function_cfg_test.cc
+++ b/src/tint/reader/spirv/function_cfg_test.cc
@@ -2210,9 +2210,7 @@
     EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 45, 40, 49, 50, 99)) << assembly;
 }
 
-// TODO(crbug.com/tint/1406): Re-enable with the typo fix (preceeded->preceded)
-// once that typo fix is rolled in Tint's SPIRV-Tools.
-TEST_F(SpvParserCFGTest, DISABLED_ComputeBlockOrder_Loop_BodyHasSwitchContinueBreak) {
+TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_BodyHasSwitchContinueBreak) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -2227,9 +2225,6 @@
      ; OpSwitch must be preceded by a selection merge
      OpSwitch %selector %99 50 %50 ; default is break, 50 is continue
 
-     %40 = OpLabel
-     OpBranch %50
-
      %50 = OpLabel
      OpBranch %20
 
@@ -2241,7 +2236,7 @@
     auto p = parser(test::Assemble(assembly));
     EXPECT_FALSE(p->Parse());
     EXPECT_FALSE(p->success());
-    EXPECT_THAT(p->error(), HasSubstr("OpSwitch must be preceeded by an OpSelectionMerge"));
+    EXPECT_THAT(p->error(), HasSubstr("OpSwitch must be preceded by an OpSelectionMerge"));
 }
 
 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_Sequence) {
@@ -2381,9 +2376,7 @@
     EXPECT_THAT(fe.block_order(), ElementsAre(10, 20, 30, 50, 99));
 }
 
-// TODO(crbug.com/tint/1406): Re-enable with the typo fix (preceeded->preceded)
-// once that typo fix is rolled in Tint's SPIRV-Tools.
-TEST_F(SpvParserCFGTest, DISABLED_ComputeBlockOrder_Loop_Continue_SwitchBreak) {
+TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Continue_SwitchBreak) {
     auto assembly = CommonTypes() + R"(
      %100 = OpFunction %void None %voidfn
 
@@ -2410,7 +2403,7 @@
     auto p = parser(test::Assemble(assembly));
     EXPECT_FALSE(p->Parse());
     EXPECT_FALSE(p->success());
-    EXPECT_THAT(p->error(), HasSubstr("OpSwitch must be preceeded by an OpSelectionMerge"));
+    EXPECT_THAT(p->error(), HasSubstr("OpSwitch must be preceded by an OpSelectionMerge"));
 }
 
 TEST_F(SpvParserCFGTest, ComputeBlockOrder_Loop_Loop) {
@@ -13028,8 +13021,8 @@
     auto p = parser(test::Assemble(assembly));
     EXPECT_FALSE(p->Parse());
     EXPECT_FALSE(p->success());
-    EXPECT_THAT(p->error(), HasSubstr("block <ID> 20[%20] exits the continue headed by <ID> "
-                                      "20[%20], but not via a structured exit"))
+    EXPECT_THAT(p->error(), HasSubstr("block <ID> '20[%20]' exits the continue headed by <ID> "
+                                      "'20[%20]', but not via a structured exit"))
         << p->error();
 }
 
diff --git a/src/tint/reader/spirv/function_memory_test.cc b/src/tint/reader/spirv/function_memory_test.cc
index 2bb98e3..dbf9219 100644
--- a/src/tint/reader/spirv/function_memory_test.cc
+++ b/src/tint/reader/spirv/function_memory_test.cc
@@ -818,11 +818,11 @@
     EXPECT_EQ(got, expected) << got;
 }
 
-TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_InferFunctionStorageClass) {
+TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_InferFunctionAddressSpace) {
     // An access chain can have no indices. When the base is a Function variable,
-    // the reference type has no explicit storage class in the AST representation.
+    // the reference type has no explicit address space in the AST representation.
     // But the pointer type for the let declaration must have an explicit
-    // 'function' storage class. From crbug.com/tint/807
+    // 'function' address space. From crbug.com/tint/807
     const std::string assembly = R"(
 OpCapability Shader
 OpMemoryModel Logical Simple
@@ -993,7 +993,7 @@
 
 TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughCopyObject_WithoutHoisting) {
     // Generates a const declaration directly.
-    // We have to do a bunch of storage class tracking for locally
+    // We have to do a bunch of address space tracking for locally
     // defined values in order to get the right pointer-to-storage-buffer
     // value type for the const declration.
     const auto assembly = OldStorageBufferPreamble() + R"(
@@ -1070,13 +1070,6 @@
     p->SkipDumpingPending("crbug.com/tint/98");
 }
 
-TEST_F(SpvParserMemoryTest, DISABLED_RemapStorageBuffer_ThroughFunctionCall) {
-    // WGSL does not support pointer-to-storage-buffer as function parameter
-}
-TEST_F(SpvParserMemoryTest, DISABLED_RemapStorageBuffer_ThroughFunctionParameter) {
-    // WGSL does not support pointer-to-storage-buffer as function parameter
-}
-
 std::string RuntimeArrayPreamble() {
     return R"(
      OpCapability Shader
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 683dd59..3c64c5b 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -1205,28 +1205,28 @@
         return nullptr;
     }
 
-    auto ast_storage_class = enum_converter_.ToStorageClass(storage_class);
-    if (ast_storage_class == ast::StorageClass::kInvalid) {
+    auto ast_address_space = enum_converter_.ToAddressSpace(storage_class);
+    if (ast_address_space == ast::AddressSpace::kInvalid) {
         Fail() << "SPIR-V pointer type with ID " << type_id << " has invalid storage class "
                << static_cast<uint32_t>(storage_class);
         return nullptr;
     }
-    if (ast_storage_class == ast::StorageClass::kUniform &&
+    if (ast_address_space == ast::AddressSpace::kUniform &&
         remap_buffer_block_type_.count(pointee_type_id)) {
-        ast_storage_class = ast::StorageClass::kStorage;
+        ast_address_space = ast::AddressSpace::kStorage;
         remap_buffer_block_type_.insert(type_id);
     }
 
     // Pipeline input and output variables map to private variables.
-    if (ast_storage_class == ast::StorageClass::kIn ||
-        ast_storage_class == ast::StorageClass::kOut) {
-        ast_storage_class = ast::StorageClass::kPrivate;
+    if (ast_address_space == ast::AddressSpace::kIn ||
+        ast_address_space == ast::AddressSpace::kOut) {
+        ast_address_space = ast::AddressSpace::kPrivate;
     }
     switch (ptr_as) {
         case PtrAs::Ref:
-            return ty_.Reference(ast_elem_ty, ast_storage_class);
+            return ty_.Reference(ast_elem_ty, ast_address_space);
         case PtrAs::Ptr:
-            return ty_.Pointer(ast_elem_ty, ast_storage_class);
+            return ty_.Pointer(ast_elem_ty, ast_address_space);
     }
     Fail() << "invalid value for ptr_as: " << static_cast<int>(ptr_as);
     return nullptr;
@@ -1443,15 +1443,15 @@
                 var.NumInOperands() > 1 ? var.GetSingleWordInOperand(1) : 0u;
             continue;
         }
-        switch (enum_converter_.ToStorageClass(spirv_storage_class)) {
-            case ast::StorageClass::kNone:
-            case ast::StorageClass::kIn:
-            case ast::StorageClass::kOut:
-            case ast::StorageClass::kUniform:
-            case ast::StorageClass::kHandle:
-            case ast::StorageClass::kStorage:
-            case ast::StorageClass::kWorkgroup:
-            case ast::StorageClass::kPrivate:
+        switch (enum_converter_.ToAddressSpace(spirv_storage_class)) {
+            case ast::AddressSpace::kNone:
+            case ast::AddressSpace::kIn:
+            case ast::AddressSpace::kOut:
+            case ast::AddressSpace::kUniform:
+            case ast::AddressSpace::kHandle:
+            case ast::AddressSpace::kStorage:
+            case ast::AddressSpace::kWorkgroup:
+            case ast::AddressSpace::kPrivate:
                 break;
             default:
                 return Fail() << "invalid SPIR-V storage class " << int(spirv_storage_class)
@@ -1481,7 +1481,7 @@
         }
 
         auto* ast_store_type = ast_type->As<Pointer>()->type;
-        auto ast_storage_class = ast_type->As<Pointer>()->storage_class;
+        auto ast_address_space = ast_type->As<Pointer>()->address_space;
         const ast::Expression* ast_constructor = nullptr;
         if (var.NumInOperands() > 1) {
             // SPIR-V initializers are always constants.
@@ -1489,7 +1489,7 @@
             // here.)
             ast_constructor = MakeConstantExpression(var.GetSingleWordInOperand(1)).expr;
         }
-        auto* ast_var = MakeVar(var.result_id(), ast_storage_class, ast_store_type, ast_constructor,
+        auto* ast_var = MakeVar(var.result_id(), ast_address_space, ast_store_type, ast_constructor,
                                 utils::Empty);
         // TODO(dneto): initializers (a.k.a. constructor expression)
         if (ast_var) {
@@ -1522,7 +1522,7 @@
         }
         auto* ast_var =
             MakeVar(builtin_position_.per_vertex_var_id,
-                    enum_converter_.ToStorageClass(builtin_position_.storage_class),
+                    enum_converter_.ToAddressSpace(builtin_position_.storage_class),
                     ConvertType(builtin_position_.position_member_type_id), ast_constructor, {});
 
         builder_.AST().AddGlobalVariable(ast_var);
@@ -1554,7 +1554,7 @@
 }
 
 ast::Var* ParserImpl::MakeVar(uint32_t id,
-                              ast::StorageClass sc,
+                              ast::AddressSpace address_space,
                               const Type* storage_type,
                               const ast::Expression* constructor,
                               AttributeList decorations) {
@@ -1564,7 +1564,7 @@
     }
 
     ast::Access access = ast::Access::kUndefined;
-    if (sc == ast::StorageClass::kStorage) {
+    if (address_space == ast::AddressSpace::kStorage) {
         bool read_only = false;
         if (auto* tn = storage_type->As<Named>()) {
             read_only = read_only_struct_types_.count(tn->name) > 0;
@@ -1575,19 +1575,19 @@
     }
 
     // Handle variables (textures and samplers) are always in the handle
-    // storage class, so we don't mention the storage class.
-    if (sc == ast::StorageClass::kHandle) {
-        sc = ast::StorageClass::kNone;
+    // address space, so we don't mention the address space.
+    if (address_space == ast::AddressSpace::kHandle) {
+        address_space = ast::AddressSpace::kNone;
     }
 
     if (!ConvertDecorationsForVariable(id, &storage_type, &decorations,
-                                       sc != ast::StorageClass::kPrivate)) {
+                                       address_space != ast::AddressSpace::kPrivate)) {
         return nullptr;
     }
 
     auto sym = builder_.Symbols().Register(namer_.Name(id));
-    return create<ast::Var>(Source{}, sym, storage_type->Build(builder_), sc, access, constructor,
-                            decorations);
+    return create<ast::Var>(Source{}, sym, storage_type->Build(builder_), address_space, access,
+                            constructor, decorations);
 }
 
 ast::Let* ParserImpl::MakeLet(uint32_t id, const Type* type, const ast::Expression* constructor) {
@@ -2486,7 +2486,7 @@
     }
 
     // Form the pointer type.
-    auto* result = ty_.Pointer(ast_store_type, ast::StorageClass::kHandle);
+    auto* result = ty_.Pointer(ast_store_type, ast::AddressSpace::kHandle);
     // Remember it for later.
     handle_type_[&var] = result;
     return result;
diff --git a/src/tint/reader/spirv/parser_impl.h b/src/tint/reader/spirv/parser_impl.h
index 948a9a8..11cbe7c 100644
--- a/src/tint/reader/spirv/parser_impl.h
+++ b/src/tint/reader/spirv/parser_impl.h
@@ -50,7 +50,7 @@
 ///
 /// A WGSL "handle" is an opaque object used for accessing a resource via
 /// special builtins.  In SPIR-V, a handle is stored a variable in the
-/// UniformConstant storage class.  The handles supported by SPIR-V are:
+/// UniformConstant address space.  The handles supported by SPIR-V are:
 ///   - images, both sampled texture and storage image
 ///   - samplers
 ///   - combined image+sampler
@@ -424,14 +424,14 @@
     /// Creates an AST 'var' node for a SPIR-V ID, including any attached decorations, unless it's
     /// an ignorable builtin variable.
     /// @param id the SPIR-V result ID
-    /// @param sc the storage class, which cannot be ast::StorageClass::kNone
+    /// @param address_space the address space, which cannot be ast::AddressSpace::kNone
     /// @param storage_type the storage type of the variable
     /// @param constructor the variable constructor
     /// @param decorations the variable decorations
     /// @returns a new Variable node, or null in the ignorable variable case and
     /// in the error case
     ast::Var* MakeVar(uint32_t id,
-                      ast::StorageClass sc,
+                      ast::AddressSpace address_space,
                       const Type* storage_type,
                       const ast::Expression* constructor,
                       AttributeList decorations);
@@ -583,7 +583,7 @@
         /// The ID of the type of a pointer to the struct in the Output storage
         /// class class.
         uint32_t pointer_type_id = 0;
-        /// The SPIR-V storage class.
+        /// The SPIR-V address space.
         SpvStorageClass storage_class = SpvStorageClassOutput;
         /// The ID of the type of a pointer to the Position member.
         uint32_t position_member_pointer_type_id = 0;
@@ -640,8 +640,8 @@
     Usage GetHandleUsage(uint32_t id) const;
 
     /// Returns the SPIR-V type for the sampler or image type for the given
-    /// variable in UniformConstant storage class, or function parameter pointing
-    /// into the UniformConstant storage class .  Returns null and emits an
+    /// variable in UniformConstant address space, or function parameter pointing
+    /// into the UniformConstant address space .  Returns null and emits an
     /// error on failure.
     /// @param var the OpVariable instruction or OpFunctionParameter
     /// @returns the Tint AST type for the sampler or texture, or null on error
@@ -649,7 +649,7 @@
         const spvtools::opt::Instruction& var);
 
     /// Returns the AST type for the pointer-to-sampler or pointer-to-texture type
-    /// for the given variable in UniformConstant storage class.  Returns null and
+    /// for the given variable in UniformConstant address space.  Returns null and
     /// emits an error on failure.
     /// @param var the OpVariable instruction
     /// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on
@@ -841,8 +841,8 @@
     // - an array, runtime array containing one of these
     // - a pointer type to one of these
     // These are the types "enclosing" a buffer block with the old style
-    // representation: using Uniform storage class and BufferBlock decoration
-    // on the struct.  The new style is to use the StorageBuffer storage class
+    // representation: using Uniform address space and BufferBlock decoration
+    // on the struct.  The new style is to use the StorageBuffer address space
     // and Block decoration.
     std::unordered_set<uint32_t> remap_buffer_block_type_;
 
diff --git a/src/tint/reader/spirv/parser_impl_convert_type_test.cc b/src/tint/reader/spirv/parser_impl_convert_type_test.cc
index 6cedddb..3b8fa62 100644
--- a/src/tint/reader/spirv/parser_impl_convert_type_test.cc
+++ b/src/tint/reader/spirv/parser_impl_convert_type_test.cc
@@ -709,11 +709,11 @@
     EXPECT_THAT(p->error(), Eq("SPIR-V pointer type with ID 3 has invalid pointee type 42"));
 }
 
-TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidStorageClass) {
-    // Disallow invalid storage class
+TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidAddressSpace) {
+    // Disallow invalid address space
     auto p = parser(test::Assemble(Preamble() + R"(
   %1 = OpTypeFloat 32
-  %3 = OpTypePointer !999 %1   ; Special syntax to inject 999 as the storage class
+  %3 = OpTypePointer !999 %1   ; Special syntax to inject 999 as the address space
   )" + MainBody()));
     // TODO(dneto): I can't get it past module building.
     EXPECT_FALSE(p->BuildInternalModule()) << p->error();
@@ -731,7 +731,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kPrivate);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -747,7 +747,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kPrivate);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -763,7 +763,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kUniform);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kUniform);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -779,7 +779,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kWorkgroup);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kWorkgroup);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -795,7 +795,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kNone);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kNone);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -811,7 +811,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kStorage);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kStorage);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -827,7 +827,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kPrivate);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -843,7 +843,7 @@
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
     EXPECT_TRUE(ptr_ty->type->Is<F32>());
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kFunction);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kFunction);
     EXPECT_TRUE(p->error().empty());
 }
 
@@ -862,12 +862,12 @@
 
     auto* ptr_ty = type->As<Pointer>();
     EXPECT_NE(ptr_ty, nullptr);
-    EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(ptr_ty->address_space, ast::AddressSpace::kPrivate);
     EXPECT_TRUE(ptr_ty->type->Is<Pointer>());
 
     auto* ptr_ptr_ty = ptr_ty->type->As<Pointer>();
     EXPECT_NE(ptr_ptr_ty, nullptr);
-    EXPECT_EQ(ptr_ptr_ty->storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(ptr_ptr_ty->address_space, ast::AddressSpace::kPrivate);
     EXPECT_TRUE(ptr_ptr_ty->type->Is<F32>());
 
     EXPECT_TRUE(p->error().empty());
diff --git a/src/tint/reader/spirv/parser_impl_handle_test.cc b/src/tint/reader/spirv/parser_impl_handle_test.cc
index cbad954..094086a 100644
--- a/src/tint/reader/spirv/parser_impl_handle_test.cc
+++ b/src/tint/reader/spirv/parser_impl_handle_test.cc
@@ -1484,10 +1484,6 @@
     }
 }
 
-// TODO(dneto): Test variable declaration and texture builtins provoked by
-// use of an image access instruction inside helper function.
-TEST_P(SpvParserHandleTest_RegisterHandleUsage_SampledImage, DISABLED_FunctionParam) {}
-
 INSTANTIATE_TEST_SUITE_P(
     ImageGather,
     SpvParserHandleTest_SampledImageAccessTest,
diff --git a/src/tint/reader/spirv/parser_impl_module_var_test.cc b/src/tint/reader/spirv/parser_impl_module_var_test.cc
index 8eb506b..44fc4b3 100644
--- a/src/tint/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/tint/reader/spirv/parser_impl_module_var_test.cc
@@ -119,7 +119,7 @@
     EXPECT_THAT(module_ast, Not(HasSubstr("Variable"))) << module_ast;
 }
 
-TEST_F(SpvModuleScopeVarParserTest, BadStorageClass_NotAWebGPUStorageClass) {
+TEST_F(SpvModuleScopeVarParserTest, BadAddressSpace_NotAWebGPUAddressSpace) {
     auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %ptr = OpTypePointer CrossWorkgroup %float
@@ -135,7 +135,7 @@
     EXPECT_THAT(p->error(), HasSubstr("unknown SPIR-V storage class: 5"));
 }
 
-TEST_F(SpvModuleScopeVarParserTest, BadStorageClass_Function) {
+TEST_F(SpvModuleScopeVarParserTest, BadAddressSpace_Function) {
     auto p = parser(test::Assemble(Preamble() + FragMain() + R"(
     %float = OpTypeFloat 32
     %ptr = OpTypePointer Function %float
@@ -1301,6 +1301,7 @@
      OpDecorate %s Block
      OpMemberDecorate %s 0 MatrixStride 8
      OpMemberDecorate %s 0 Offset 0
+     OpMemberDecorate %s 0 ColMajor
      %void = OpTypeVoid
      %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
@@ -1330,6 +1331,7 @@
      OpDecorate %s Block
      OpMemberDecorate %s 0 MatrixStride 64
      OpMemberDecorate %s 0 Offset 0
+     OpMemberDecorate %s 0 ColMajor
      %void = OpTypeVoid
      %voidfn = OpTypeFunction %void
      %float = OpTypeFloat 32
@@ -1794,7 +1796,7 @@
     // as a function parameter.
     EXPECT_FALSE(p->Parse());
     EXPECT_FALSE(p->success());
-    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand 1"));
+    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand '1"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_Load_Direct) {
@@ -1904,7 +1906,7 @@
     // This example is invalid because you can't pass pointer-to-Input
     // as a function parameter.
     EXPECT_FALSE(p->Parse());
-    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand 1"));
+    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand '1"));
 }
 
 // Returns the start of a shader for testing SampleMask
@@ -2806,7 +2808,7 @@
     // This example is invalid because you can't pass pointer-to-Input
     // as a function parameter.
     EXPECT_FALSE(p->Parse());
-    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand 1"));
+    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand '1"));
 }
 
 // Returns the start of a shader for testing InstanceIndex,
@@ -2964,7 +2966,7 @@
     // This example is invalid because you can't pass pointer-to-Input
     // as a function parameter.
     EXPECT_FALSE(p->Parse());
-    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand 1"));
+    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand '1"));
 }
 
 TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_Direct) {
@@ -3098,7 +3100,7 @@
     // This example is invalid because you can't pass pointer-to-Input
     // as a function parameter.
     EXPECT_FALSE(p->Parse());
-    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand 1"));
+    EXPECT_THAT(p->error(), HasSubstr("Invalid storage class for pointer operand '1"));
 }
 
 // Returns the start of a shader for testing LocalInvocationIndex,
diff --git a/src/tint/reader/spirv/parser_type.cc b/src/tint/reader/spirv/parser_type.cc
index 5de62f6..256444a 100644
--- a/src/tint/reader/spirv/parser_type.cc
+++ b/src/tint/reader/spirv/parser_type.cc
@@ -50,11 +50,11 @@
 
 namespace {
 struct PointerHasher {
-    size_t operator()(const Pointer& t) const { return utils::Hash(t.type, t.storage_class); }
+    size_t operator()(const Pointer& t) const { return utils::Hash(t.type, t.address_space); }
 };
 
 struct ReferenceHasher {
-    size_t operator()(const Reference& t) const { return utils::Hash(t.type, t.storage_class); }
+    size_t operator()(const Reference& t) const { return utils::Hash(t.type, t.address_space); }
 };
 
 struct VectorHasher {
@@ -107,10 +107,10 @@
 // Equality operators
 //! @cond Doxygen_Suppress
 static bool operator==(const Pointer& a, const Pointer& b) {
-    return a.type == b.type && a.storage_class == b.storage_class;
+    return a.type == b.type && a.address_space == b.address_space;
 }
 static bool operator==(const Reference& a, const Reference& b) {
-    return a.type == b.type && a.storage_class == b.storage_class;
+    return a.type == b.type && a.address_space == b.address_space;
 }
 static bool operator==(const Vector& a, const Vector& b) {
     return a.type == b.type && a.size == b.size;
@@ -170,14 +170,14 @@
 
 Texture::~Texture() = default;
 
-Pointer::Pointer(const Type* t, ast::StorageClass s) : type(t), storage_class(s) {}
+Pointer::Pointer(const Type* t, ast::AddressSpace s) : type(t), address_space(s) {}
 Pointer::Pointer(const Pointer&) = default;
 
 const ast::Type* Pointer::Build(ProgramBuilder& b) const {
-    return b.ty.pointer(type->Build(b), storage_class);
+    return b.ty.pointer(type->Build(b), address_space);
 }
 
-Reference::Reference(const Type* t, ast::StorageClass s) : type(t), storage_class(s) {}
+Reference::Reference(const Type* t, ast::AddressSpace s) : type(t), address_space(s) {}
 Reference::Reference(const Reference&) = default;
 
 const ast::Type* Reference::Build(ProgramBuilder& b) const {
@@ -438,12 +438,12 @@
     return state->i32_;
 }
 
-const spirv::Pointer* TypeManager::Pointer(const Type* el, ast::StorageClass sc) {
-    return state->pointers_.Get(el, sc);
+const spirv::Pointer* TypeManager::Pointer(const Type* el, ast::AddressSpace address_space) {
+    return state->pointers_.Get(el, address_space);
 }
 
-const spirv::Reference* TypeManager::Reference(const Type* el, ast::StorageClass sc) {
-    return state->references_.Get(el, sc);
+const spirv::Reference* TypeManager::Reference(const Type* el, ast::AddressSpace address_space) {
+    return state->references_.Get(el, address_space);
 }
 
 const spirv::Vector* TypeManager::Vector(const Type* el, uint32_t size) {
@@ -519,13 +519,13 @@
 
 std::string Pointer::String() const {
     std::stringstream ss;
-    ss << "ptr<" << utils::ToString(storage_class) << ", " << type->String() + ">";
+    ss << "ptr<" << utils::ToString(address_space) << ", " << type->String() + ">";
     return ss.str();
 }
 
 std::string Reference::String() const {
     std::stringstream ss;
-    ss << "ref<" + utils::ToString(storage_class) << ", " << type->String() << ">";
+    ss << "ref<" + utils::ToString(address_space) << ", " << type->String() << ">";
     return ss.str();
 }
 
diff --git a/src/tint/reader/spirv/parser_type.h b/src/tint/reader/spirv/parser_type.h
index 9543b51..4c67f7a 100644
--- a/src/tint/reader/spirv/parser_type.h
+++ b/src/tint/reader/spirv/parser_type.h
@@ -20,8 +20,8 @@
 #include <vector>
 
 #include "src/tint/ast/access.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/ast/sampler.h"
-#include "src/tint/ast/storage_class.h"
 #include "src/tint/ast/storage_texture.h"
 #include "src/tint/ast/texture.h"
 #include "src/tint/castable.h"
@@ -161,8 +161,8 @@
 struct Pointer final : public Castable<Pointer, Type> {
     /// Constructor
     /// @param ty the store type
-    /// @param sc the pointer storage class
-    Pointer(const Type* ty, ast::StorageClass sc);
+    /// @param sc the pointer address space
+    Pointer(const Type* ty, ast::AddressSpace sc);
 
     /// Copy constructor
     /// @param other the other type to copy
@@ -179,8 +179,8 @@
 
     /// the store type
     Type const* const type;
-    /// the pointer storage class
-    ast::StorageClass const storage_class;
+    /// the pointer address space
+    ast::AddressSpace const address_space;
 };
 
 /// `ref<SC, T>` type
@@ -189,8 +189,8 @@
 struct Reference final : public Castable<Reference, Type> {
     /// Constructor
     /// @param ty the referenced type
-    /// @param sc the reference storage class
-    Reference(const Type* ty, ast::StorageClass sc);
+    /// @param sc the reference address space
+    Reference(const Type* ty, ast::AddressSpace sc);
 
     /// Copy constructor
     /// @param other the other type to copy
@@ -207,8 +207,8 @@
 
     /// the store type
     Type const* const type;
-    /// the pointer storage class
-    ast::StorageClass const storage_class;
+    /// the pointer address space
+    ast::AddressSpace const address_space;
 };
 
 /// `vecN<T>` type
@@ -534,15 +534,15 @@
     /// @return a I32 type. Repeated calls will return the same pointer.
     const spirv::I32* I32();
     /// @param ty the store type
-    /// @param sc the pointer storage class
+    /// @param address_space the pointer address space
     /// @return a Pointer type. Repeated calls with the same arguments will return
     /// the same pointer.
-    const spirv::Pointer* Pointer(const Type* ty, ast::StorageClass sc);
+    const spirv::Pointer* Pointer(const Type* ty, ast::AddressSpace address_space);
     /// @param ty the referenced type
-    /// @param sc the reference storage class
+    /// @param address_space the reference address space
     /// @return a Reference type. Repeated calls with the same arguments will
     /// return the same pointer.
-    const spirv::Reference* Reference(const Type* ty, ast::StorageClass sc);
+    const spirv::Reference* Reference(const Type* ty, ast::AddressSpace address_space);
     /// @param ty the element type
     /// @param sz the number of elements in the vector
     /// @return a Vector type. Repeated calls with the same arguments will return
diff --git a/src/tint/reader/spirv/parser_type_test.cc b/src/tint/reader/spirv/parser_type_test.cc
index 37ac113..90a8da2 100644
--- a/src/tint/reader/spirv/parser_type_test.cc
+++ b/src/tint/reader/spirv/parser_type_test.cc
@@ -28,8 +28,8 @@
     EXPECT_EQ(ty.U32(), ty.U32());
     EXPECT_EQ(ty.F32(), ty.F32());
     EXPECT_EQ(ty.I32(), ty.I32());
-    EXPECT_EQ(ty.Pointer(ty.I32(), ast::StorageClass::kNone),
-              ty.Pointer(ty.I32(), ast::StorageClass::kNone));
+    EXPECT_EQ(ty.Pointer(ty.I32(), ast::AddressSpace::kNone),
+              ty.Pointer(ty.I32(), ast::AddressSpace::kNone));
     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));
@@ -53,10 +53,10 @@
     Symbol sym_b(Symbol(2, {}));
 
     TypeManager ty;
-    EXPECT_NE(ty.Pointer(ty.I32(), ast::StorageClass::kNone),
-              ty.Pointer(ty.U32(), ast::StorageClass::kNone));
-    EXPECT_NE(ty.Pointer(ty.I32(), ast::StorageClass::kNone),
-              ty.Pointer(ty.I32(), ast::StorageClass::kIn));
+    EXPECT_NE(ty.Pointer(ty.I32(), ast::AddressSpace::kNone),
+              ty.Pointer(ty.U32(), ast::AddressSpace::kNone));
+    EXPECT_NE(ty.Pointer(ty.I32(), ast::AddressSpace::kNone),
+              ty.Pointer(ty.I32(), ast::AddressSpace::kIn));
     EXPECT_NE(ty.Vector(ty.I32(), 3), ty.Vector(ty.U32(), 3));
     EXPECT_NE(ty.Vector(ty.I32(), 3), ty.Vector(ty.I32(), 2));
     EXPECT_NE(ty.Matrix(ty.I32(), 3, 2), ty.Matrix(ty.U32(), 3, 2));
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 2c72d6a..50dd191 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -206,12 +206,12 @@
 
 ParserImpl::VarDeclInfo::VarDeclInfo(Source source_in,
                                      std::string name_in,
-                                     ast::StorageClass storage_class_in,
+                                     ast::AddressSpace address_space_in,
                                      ast::Access access_in,
                                      const ast::Type* type_in)
     : source(std::move(source_in)),
       name(std::move(name_in)),
-      storage_class(storage_class_in),
+      address_space(address_space_in),
       access(access_in),
       type(type_in) {}
 
@@ -594,7 +594,7 @@
     return create<ast::Var>(decl->source,                             // source
                             builder_.Symbols().Register(decl->name),  // symbol
                             decl->type,                               // type
-                            decl->storage_class,                      // storage class
+                            decl->address_space,                      // address space
                             decl->access,                             // access control
                             initializer,                              // initializer
                             std::move(attrs));                        // attributes
@@ -697,14 +697,14 @@
         return Failure::kErrored;
     }
 
-    return VarDeclInfo{decl->source, decl->name, vq.storage_class, vq.access, decl->type};
+    return VarDeclInfo{decl->source, decl->name, vq.address_space, vq.access, decl->type};
 }
 
 // texture_and_sampler_types
 //  : sampler_type
 //  | depth_texture_type
-//  | sampled_texture_type LESS_THAN type_decl GREATER_THAN
-//  | multisampled_texture_type LESS_THAN type_decl GREATER_THAN
+//  | sampled_texture_type LESS_THAN type_specifier GREATER_THAN
+//  | multisampled_texture_type LESS_THAN type_specifier GREATER_THAN
 //  | storage_texture_type LESS_THAN texel_format
 //                         COMMA access_mode GREATER_THAN
 Maybe<const ast::Type*> ParserImpl::texture_and_sampler_types() {
@@ -928,7 +928,7 @@
     return fmt;
 }
 
-Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_optional_type_decl(
+Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_optional_type_specifier(
     std::string_view use,
     bool allow_inferred) {
     auto ident = expect_ident(use);
@@ -945,7 +945,7 @@
     }
 
     auto& t = peek();
-    auto type = type_decl();
+    auto type = type_specifier();
     if (type.errored) {
         return Failure::kErrored;
     }
@@ -960,13 +960,14 @@
 //   : ident ( COLON typed_decl ) ?
 Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_optionally_typed_ident(
     std::string_view use) {
-    return expect_ident_with_optional_type_decl(use, true);
+    return expect_ident_with_optional_type_specifier(use, /* allow_inferred */ true);
 }
 
-// ident_with_type_decl
-//   : IDENT COLON type_decl
-Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_type_decl(std::string_view use) {
-    return expect_ident_with_optional_type_decl(use, false);
+// ident_with_type_specifier
+//   : IDENT COLON type_specifier
+Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_type_specifier(
+    std::string_view use) {
+    return expect_ident_with_optional_type_specifier(use, /* allow_inferred */ false);
 }
 
 // access_mode
@@ -1025,7 +1026,7 @@
 }
 
 // type_alias_decl
-//   : TYPE IDENT EQUAL type_decl
+//   : TYPE IDENT EQUAL type_specifier
 Maybe<const ast::Alias*> ParserImpl::type_alias_decl() {
     if (!peek_is(Token::Type::kType)) {
         return Failure::kNoMatch;
@@ -1043,7 +1044,7 @@
         return Failure::kErrored;
     }
 
-    auto type = type_decl();
+    auto type = type_specifier();
     if (type.errored) {
         return Failure::kErrored;
     }
@@ -1106,19 +1107,19 @@
     return MatrixDimensions{columns, 2};
 }
 
-// type_decl_without_ident:
+// type_specifier_without_ident:
 //   : BOOL
 //   | F16
 //   | F32
 //   | I32
 //   | U32
-//   | ARRAY LESS_THAN type_decl ( COMMA element_count_expression )? GREATER_THAN
-//   | ATOMIC LESS_THAN type_decl GREATER_THAN
-//   | PTR LESS_THAN address_space COMMA type_decl ( COMMA access_mode )? GREATER_THAN
-//   | mat_prefix LESS_THAN type_decl GREATER_THAN
-//   | vec_prefix LESS_THAN type_decl GREATER_THAN
+//   | ARRAY LESS_THAN type_specifier ( COMMA element_count_expression )? GREATER_THAN
+//   | ATOMIC LESS_THAN type_specifier GREATER_THAN
+//   | PTR LESS_THAN address_space COMMA type_specifier ( COMMA access_mode )? GREATER_THAN
+//   | mat_prefix LESS_THAN type_specifier GREATER_THAN
+//   | vec_prefix LESS_THAN type_specifier GREATER_THAN
 //   | texture_and_sampler_types
-Maybe<const ast::Type*> ParserImpl::type_decl_without_ident() {
+Maybe<const ast::Type*> ParserImpl::type_specifier_without_ident() {
     auto& t = peek();
 
     if (match(Token::Type::kBool)) {
@@ -1143,29 +1144,29 @@
 
     if (t.Is(Token::Type::kArray) && peek_is(Token::Type::kLessThan, 1)) {
         if (match(Token::Type::kArray)) {
-            return expect_type_decl_array(t.source());
+            return expect_type_specifier_array(t.source());
         }
     }
 
     if (match(Token::Type::kAtomic)) {
-        return expect_type_decl_atomic(t.source());
+        return expect_type_specifier_atomic(t.source());
     }
 
     if (match(Token::Type::kPtr)) {
-        return expect_type_decl_pointer(t.source());
+        return expect_type_specifier_pointer(t.source());
     }
 
     if (t.IsMatrix() && peek_is(Token::Type::kLessThan, 1)) {
         auto mat = mat_prefix();
         if (mat.matched) {
-            return expect_type_decl_matrix(t.source(), mat.value);
+            return expect_type_specifier_matrix(t.source(), mat.value);
         }
     }
 
     if (t.IsVector() && peek_is(Token::Type::kLessThan, 1)) {
         auto vec = vec_prefix();
         if (vec.matched) {
-            return expect_type_decl_vector(t.source(), vec.value);
+            return expect_type_specifier_vector(t.source(), vec.value);
         }
     }
 
@@ -1180,21 +1181,21 @@
     return Failure::kNoMatch;
 }
 
-// type_decl
+// type_specifier
 //   : IDENTIFIER
-//   | type_decl_without_ident
-Maybe<const ast::Type*> ParserImpl::type_decl() {
+//   | type_specifier_without_ident
+Maybe<const ast::Type*> ParserImpl::type_specifier() {
     auto& t = peek();
     Source source;
     if (match(Token::Type::kIdentifier, &source)) {
         return builder_.create<ast::TypeName>(source, builder_.Symbols().Register(t.to_str()));
     }
 
-    return type_decl_without_ident();
+    return type_specifier_without_ident();
 }
 
 Expect<const ast::Type*> ParserImpl::expect_type(std::string_view use) {
-    auto type = type_decl();
+    auto type = type_specifier();
     if (type.errored) {
         return Failure::kErrored;
     }
@@ -1204,11 +1205,11 @@
     return type.value;
 }
 
-// LESS_THAN address_space COMMA type_decl ( COMMA access_mode )? GREATER_THAN
-Expect<const ast::Type*> ParserImpl::expect_type_decl_pointer(const Source& s) {
+// LESS_THAN address_space COMMA type_specifier ( COMMA access_mode )? GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_specifier_pointer(const Source& s) {
     const char* use = "ptr declaration";
 
-    auto storage_class = ast::StorageClass::kNone;
+    auto address_space = ast::AddressSpace::kNone;
     auto access = ast::Access::kUndefined;
 
     auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
@@ -1216,7 +1217,7 @@
         if (sc.errored) {
             return Failure::kErrored;
         }
-        storage_class = sc.value;
+        address_space = sc.value;
 
         if (!expect(use, Token::Type::kComma)) {
             return Failure::kErrored;
@@ -1242,11 +1243,11 @@
         return Failure::kErrored;
     }
 
-    return builder_.ty.pointer(make_source_range_from(s), subtype.value, storage_class, access);
+    return builder_.ty.pointer(make_source_range_from(s), subtype.value, address_space, access);
 }
 
-// LESS_THAN type_decl GREATER_THAN
-Expect<const ast::Type*> ParserImpl::expect_type_decl_atomic(const Source& s) {
+// LESS_THAN type_specifier GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_specifier_atomic(const Source& s) {
     const char* use = "atomic declaration";
 
     auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
@@ -1257,8 +1258,8 @@
     return builder_.ty.atomic(make_source_range_from(s), subtype.value);
 }
 
-// LESS_THAN type_decl GREATER_THAN
-Expect<const ast::Type*> ParserImpl::expect_type_decl_vector(const Source& s, uint32_t count) {
+// LESS_THAN type_specifier GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_specifier_vector(const Source& s, uint32_t count) {
     const char* use = "vector";
     auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
     if (ty.errored) {
@@ -1268,8 +1269,8 @@
     return builder_.ty.vec(make_source_range_from(s), ty.value, count);
 }
 
-// LESS_THAN type_decl ( COMMA element_count_expression )? GREATER_THAN
-Expect<const ast::Type*> ParserImpl::expect_type_decl_array(const Source& s) {
+// LESS_THAN type_specifier ( COMMA element_count_expression )? GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_specifier_array(const Source& s) {
     const char* use = "array declaration";
 
     struct TypeAndSize {
@@ -1309,9 +1310,9 @@
     return builder_.ty.array(make_source_range_from(s), type_size->type, type_size->size);
 }
 
-// LESS_THAN type_decl GREATER_THAN
-Expect<const ast::Type*> ParserImpl::expect_type_decl_matrix(const Source& s,
-                                                             const MatrixDimensions& dims) {
+// LESS_THAN type_specifier GREATER_THAN
+Expect<const ast::Type*> ParserImpl::expect_type_specifier_matrix(const Source& s,
+                                                                  const MatrixDimensions& dims) {
     const char* use = "matrix";
     auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
     if (ty.errored) {
@@ -1329,19 +1330,19 @@
 //   | 'storage'
 //
 // Note, we also parse `push_constant` from the experimental extension
-Expect<ast::StorageClass> ParserImpl::expect_address_space(std::string_view use) {
+Expect<ast::AddressSpace> ParserImpl::expect_address_space(std::string_view use) {
     auto& t = peek();
-    auto ident = expect_ident("storage class");
+    auto ident = expect_ident("address space");
     if (ident.errored) {
         return Failure::kErrored;
     }
 
-    auto storage_class = ast::ParseStorageClass(ident.value);
-    if (storage_class == ast::StorageClass::kInvalid) {
-        return add_error(t.source(), "invalid storage class", use);
+    auto address_space = ast::ParseAddressSpace(ident.value);
+    if (address_space == ast::AddressSpace::kInvalid) {
+        return add_error(t.source(), "invalid address space", use);
     }
 
-    return {storage_class, t.source()};
+    return {address_space, t.source()};
 }
 
 // struct_decl
@@ -1402,14 +1403,14 @@
 }
 
 // struct_member
-//   : attribute* ident_with_type_decl
+//   : attribute* ident_with_type_specifier
 Expect<ast::StructMember*> ParserImpl::expect_struct_member() {
     auto attrs = attribute_list();
     if (attrs.errored) {
         return Failure::kErrored;
     }
 
-    auto decl = expect_ident_with_type_decl("struct member");
+    auto decl = expect_ident_with_type_specifier("struct member");
     if (decl.errored) {
         return Failure::kErrored;
     }
@@ -1477,10 +1478,10 @@
 }
 
 // function_header
-//   : FN IDENT PAREN_LEFT param_list PAREN_RIGHT return_type_decl_optional
-// return_type_decl_optional
+//   : FN IDENT PAREN_LEFT param_list PAREN_RIGHT return_type_specifier_optional
+// return_type_specifier_optional
 //   :
-//   | ARROW attribute_list* type_decl
+//   | ARROW attribute_list* type_specifier
 Maybe<ParserImpl::FunctionHeader> ParserImpl::function_header() {
     Source source;
     if (!match(Token::Type::kFn, &source)) {
@@ -1516,7 +1517,7 @@
         }
         return_attributes = attrs.value;
 
-        auto type = type_decl();
+        auto type = type_specifier();
         if (type.errored) {
             errored = true;
         } else if (!type.matched) {
@@ -1565,11 +1566,11 @@
 }
 
 // param
-//   : attribute_list* ident COLON type_decl
+//   : attribute_list* ident COLON type_specifier
 Expect<ast::Parameter*> ParserImpl::expect_param() {
     auto attrs = attribute_list();
 
-    auto decl = expect_ident_with_type_decl("parameter");
+    auto decl = expect_ident_with_type_specifier("parameter");
     if (decl.errored) {
         return Failure::kErrored;
     }
@@ -1988,7 +1989,7 @@
     auto* var = create<ast::Var>(decl->source,                             // source
                                  builder_.Symbols().Register(decl->name),  // symbol
                                  decl->type,                               // type
-                                 decl->storage_class,                      // storage class
+                                 decl->address_space,                      // address space
                                  decl->access,                             // access control
                                  initializer,                              // initializer
                                  utils::Empty);                            // attributes
@@ -2468,7 +2469,7 @@
 }
 
 // callable
-//   : type_decl_without_ident
+//   : type_specifier_without_ident
 //   | ARRAY
 //   | mat_prefix
 //   | vec_prefix
@@ -2479,10 +2480,10 @@
 Maybe<const ast::Type*> ParserImpl::callable() {
     auto& t = peek();
 
-    //  This _must_ match `type_decl_without_ident` before any of the other types as they're all
-    //  prefixes of the types and we want to match the longer `vec3<f32>` then the shorter
+    //  This _must_ match `type_specifier_without_ident` before any of the other types as they're
+    //  all prefixes of the types and we want to match the longer `vec3<f32>` then the shorter
     //  prefix match of `vec3`.
-    auto ty = type_decl_without_ident();
+    auto ty = type_specifier_without_ident();
     if (ty.errored) {
         return Failure::kErrored;
     }
@@ -2509,7 +2510,7 @@
 }
 
 // primary_expression
-//   : BITCAST LESS_THAN type_decl GREATER_THAN paren_expression
+//   : BITCAST LESS_THAN type_specifier GREATER_THAN paren_expression
 //   | callable argument_expression_list
 //   | const_literal
 //   | IDENT argument_expression_list?
@@ -3435,15 +3436,16 @@
     if (t == "align") {
         const char* use = "align attribute";
         return expect_paren_block(use, [&]() -> Result {
-            auto val = expect_positive_sint(use);
-            if (val.errored) {
+            auto expr = expression();
+            if (expr.errored) {
                 return Failure::kErrored;
             }
+            if (!expr.matched) {
+                return add_error(peek(), "expected align expression");
+            }
             match(Token::Type::kComma);
 
-            return create<ast::StructMemberAlignAttribute>(
-                t.source(), create<ast::IntLiteralExpression>(
-                                val.value, ast::IntLiteralExpression::Suffix::kNone));
+            return create<ast::StructMemberAlignAttribute>(t.source(), expr.value);
         });
     }
 
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index cc0cd2b..5ae4c97 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -278,12 +278,12 @@
         /// Constructor
         /// @param source_in variable declaration source
         /// @param name_in variable name
-        /// @param storage_class_in variable storage class
+        /// @param address_space_in variable address space
         /// @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::AddressSpace address_space_in,
                     ast::Access access_in,
                     const ast::Type* type_in);
         /// Destructor
@@ -293,8 +293,8 @@
         Source source;
         /// Variable name
         std::string name;
-        /// Variable storage class
-        ast::StorageClass storage_class = ast::StorageClass::kNone;
+        /// Variable address space
+        ast::AddressSpace address_space = ast::AddressSpace::kNone;
         /// Variable access control
         ast::Access access = ast::Access::kUndefined;
         /// Variable type
@@ -303,8 +303,8 @@
 
     /// 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 address space
+        ast::AddressSpace address_space = ast::AddressSpace::kNone;
         /// The variable's access control
         ast::Access access = ast::Access::kUndefined;
     };
@@ -427,8 +427,8 @@
     /// @param use a description of what was being parsed if an error was raised.
     /// @param allow_inferred allow the identifier to be parsed without a type
     /// @returns the parsed identifier, and possibly type, or empty otherwise
-    Expect<TypedIdentifier> expect_ident_with_optional_type_decl(std::string_view use,
-                                                                 bool allow_inferred);
+    Expect<TypedIdentifier> expect_ident_with_optional_type_specifier(std::string_view use,
+                                                                      bool allow_inferred);
     /// Parses a `ident` or a `variable_ident_decl` grammar element, erroring on parse failure.
     /// @param use a description of what was being parsed if an error was raised.
     /// @returns the identifier or empty otherwise.
@@ -436,7 +436,7 @@
     /// Parses a `variable_ident_decl` grammar element, erroring on parse failure.
     /// @param use a description of what was being parsed if an error was raised.
     /// @returns the identifier and type parsed or empty otherwise
-    Expect<TypedIdentifier> expect_ident_with_type_decl(std::string_view use);
+    Expect<TypedIdentifier> expect_ident_with_type_specifier(std::string_view use);
     /// Parses a `variable_qualifier` grammar element
     /// @returns the variable qualifier information
     Maybe<VariableQualifier> variable_qualifier();
@@ -452,16 +452,16 @@
     /// Parses a `mat_prefix` grammar element
     /// @returns the matrix dimensions or nullptr
     Maybe<MatrixDimensions> mat_prefix();
-    /// Parses a `type_decl_without_ident` grammar element
+    /// Parses a `type_specifier_without_ident` grammar element
     /// @returns the parsed Type or nullptr if none matched.
-    Maybe<const ast::Type*> type_decl_without_ident();
-    /// Parses a `type_decl` grammar element
+    Maybe<const ast::Type*> type_specifier_without_ident();
+    /// Parses a `type_specifier` grammar element
     /// @returns the parsed Type or nullptr if none matched.
-    Maybe<const ast::Type*> type_decl();
+    Maybe<const ast::Type*> type_specifier();
     /// Parses an `address_space` grammar element, erroring on parse failure.
     /// @param use a description of what was being parsed if an error was raised.
-    /// @returns the address space or StorageClass::kNone if none matched
-    Expect<ast::StorageClass> expect_address_space(std::string_view use);
+    /// @returns the address space or ast::AddressSpace::kNone if none matched
+    Expect<ast::AddressSpace> expect_address_space(std::string_view use);
     /// Parses a `struct_decl` grammar element.
     /// @returns the struct type or nullptr on error
     Maybe<const ast::Struct*> struct_decl();
@@ -861,11 +861,12 @@
     /// Used to ensure that all attributes are consumed.
     bool expect_attributes_consumed(utils::VectorRef<const ast::Attribute*> list);
 
-    Expect<const ast::Type*> expect_type_decl_pointer(const Source& s);
-    Expect<const ast::Type*> expect_type_decl_atomic(const Source& s);
-    Expect<const ast::Type*> expect_type_decl_vector(const Source& s, uint32_t count);
-    Expect<const ast::Type*> expect_type_decl_array(const Source& s);
-    Expect<const ast::Type*> expect_type_decl_matrix(const Source& s, const MatrixDimensions& dims);
+    Expect<const ast::Type*> expect_type_specifier_pointer(const Source& s);
+    Expect<const ast::Type*> expect_type_specifier_atomic(const Source& s);
+    Expect<const ast::Type*> expect_type_specifier_vector(const Source& s, uint32_t count);
+    Expect<const ast::Type*> expect_type_specifier_array(const Source& s);
+    Expect<const ast::Type*> expect_type_specifier_matrix(const Source& s,
+                                                          const MatrixDimensions& dims);
 
     Expect<const ast::Type*> expect_type(std::string_view use);
 
diff --git a/src/tint/reader/wgsl/parser_impl_storage_class_test.cc b/src/tint/reader/wgsl/parser_impl_address_space_test.cc
similarity index 60%
rename from src/tint/reader/wgsl/parser_impl_storage_class_test.cc
rename to src/tint/reader/wgsl/parser_impl_address_space_test.cc
index c6ef7eb..d9d6d5e 100644
--- a/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_address_space_test.cc
@@ -17,18 +17,18 @@
 namespace tint::reader::wgsl {
 namespace {
 
-struct StorageClassData {
+struct AddressSpaceData {
     const char* input;
-    ast::StorageClass result;
+    ast::AddressSpace result;
 };
-inline std::ostream& operator<<(std::ostream& out, StorageClassData data) {
+inline std::ostream& operator<<(std::ostream& out, AddressSpaceData data) {
     out << std::string(data.input);
     return out;
 }
 
-class ParserStorageClassTest : public ParserImplTestWithParam<StorageClassData> {};
+class ParserAddressSpaceTest : public ParserImplTestWithParam<AddressSpaceData> {};
 
-TEST_P(ParserStorageClassTest, Parses) {
+TEST_P(ParserAddressSpaceTest, Parses) {
     auto params = GetParam();
     auto p = parser(params.input);
 
@@ -42,19 +42,19 @@
 }
 INSTANTIATE_TEST_SUITE_P(
     ParserImplTest,
-    ParserStorageClassTest,
-    testing::Values(StorageClassData{"uniform", ast::StorageClass::kUniform},
-                    StorageClassData{"workgroup", ast::StorageClass::kWorkgroup},
-                    StorageClassData{"storage", ast::StorageClass::kStorage},
-                    StorageClassData{"private", ast::StorageClass::kPrivate},
-                    StorageClassData{"function", ast::StorageClass::kFunction}));
+    ParserAddressSpaceTest,
+    testing::Values(AddressSpaceData{"uniform", ast::AddressSpace::kUniform},
+                    AddressSpaceData{"workgroup", ast::AddressSpace::kWorkgroup},
+                    AddressSpaceData{"storage", ast::AddressSpace::kStorage},
+                    AddressSpaceData{"private", ast::AddressSpace::kPrivate},
+                    AddressSpaceData{"function", ast::AddressSpace::kFunction}));
 
-TEST_F(ParserImplTest, StorageClass_NoMatch) {
-    auto p = parser("not-a-storage-class");
+TEST_F(ParserImplTest, AddressSpace_NoMatch) {
+    auto p = parser("not-a-address-space");
     auto sc = p->expect_address_space("test");
     EXPECT_EQ(sc.errored, true);
     EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:1: invalid storage class for test");
+    EXPECT_EQ(p->error(), "1:1: invalid address space for test");
 }
 
 }  // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index ac2161c..7ea5aaa 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -851,17 +851,9 @@
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignInvaldValue) {
-    EXPECT("struct S { @align(x) i : i32, };",
-           R"(test.wgsl:1:19 error: expected signed integer literal for align attribute
-struct S { @align(x) i : i32, };
-                  ^
-)");
-}
-
-TEST_F(ParserImplErrorTest, GlobalDeclStructMemberAlignNegativeValue) {
-    EXPECT("struct S { @align(-2) i : i32, };",
-           R"(test.wgsl:1:19 error: align attribute must be positive
-struct S { @align(-2) i : i32, };
+    EXPECT("struct S { @align(fn) i : i32, };",
+           R"(test.wgsl:1:19 error: expected align expression
+struct S { @align(fn) i : i32, };
                   ^^
 )");
 }
@@ -1113,9 +1105,9 @@
 )");
 }
 
-TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingStorageClass) {
+TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingAddressSpace) {
     EXPECT("var i : ptr<meow, u32>;",
-           R"(test.wgsl:1:13 error: invalid storage class for ptr declaration
+           R"(test.wgsl:1:13 error: invalid address space for ptr declaration
 var i : ptr<meow, u32>;
             ^^^^
 )");
@@ -1147,7 +1139,7 @@
 
 TEST_F(ParserImplErrorTest, GlobalDeclVarStorageDeclInvalidClass) {
     EXPECT("var<fish> i : i32",
-           R"(test.wgsl:1:5 error: invalid storage class for variable declaration
+           R"(test.wgsl:1:5 error: invalid address space for variable declaration
 var<fish> i : i32
     ^^^^
 )");
diff --git a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
index 85f4702..d6e475a 100644
--- a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
@@ -31,7 +31,7 @@
 
     EXPECT_EQ(var->symbol, p->builder().Symbols().Get("a"));
     EXPECT_TRUE(var->type->Is<ast::F32>());
-    EXPECT_EQ(var->declared_storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(var->declared_address_space, ast::AddressSpace::kPrivate);
 
     EXPECT_EQ(var->source.range.begin.line, 1u);
     EXPECT_EQ(var->source.range.begin.column, 14u);
@@ -55,7 +55,7 @@
 
     EXPECT_EQ(var->symbol, p->builder().Symbols().Get("a"));
     EXPECT_TRUE(var->type->Is<ast::F32>());
-    EXPECT_EQ(var->declared_storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(var->declared_address_space, ast::AddressSpace::kPrivate);
 
     EXPECT_EQ(var->source.range.begin.line, 1u);
     EXPECT_EQ(var->source.range.begin.column, 14u);
@@ -81,7 +81,7 @@
     EXPECT_EQ(var->symbol, p->builder().Symbols().Get("a"));
     ASSERT_NE(var->type, nullptr);
     EXPECT_TRUE(var->type->Is<ast::F32>());
-    EXPECT_EQ(var->declared_storage_class, ast::StorageClass::kUniform);
+    EXPECT_EQ(var->declared_address_space, ast::AddressSpace::kUniform);
 
     EXPECT_EQ(var->source.range.begin.line, 1u);
     EXPECT_EQ(var->source.range.begin.column, 36u);
@@ -112,7 +112,7 @@
     EXPECT_EQ(var->symbol, p->builder().Symbols().Get("a"));
     ASSERT_NE(var->type, nullptr);
     EXPECT_TRUE(var->type->Is<ast::F32>());
-    EXPECT_EQ(var->declared_storage_class, ast::StorageClass::kUniform);
+    EXPECT_EQ(var->declared_address_space, ast::AddressSpace::kUniform);
 
     EXPECT_EQ(var->source.range.begin.line, 1u);
     EXPECT_EQ(var->source.range.begin.column, 36u);
@@ -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 declaration");
+    EXPECT_EQ(p->error(), "1:5: invalid address space for variable declaration");
 }
 
 }  // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_struct_body_decl_test.cc b/src/tint/reader/wgsl/parser_impl_struct_body_decl_test.cc
index 3499b5c..ce56a67 100644
--- a/src/tint/reader/wgsl/parser_impl_struct_body_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_struct_body_decl_test.cc
@@ -60,12 +60,12 @@
 TEST_F(ParserImplTest, StructBodyDecl_InvalidAlign) {
     auto p = parser(R"(
 {
-  @align(nan) a : i32,
+  @align(if) a : i32,
 })");
     auto m = p->expect_struct_body_decl();
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(m.errored);
-    EXPECT_EQ(p->error(), "3:10: expected signed integer literal for align attribute");
+    EXPECT_EQ(p->error(), "3:10: expected align expression");
 }
 
 TEST_F(ParserImplTest, StructBodyDecl_InvalidSize) {
diff --git a/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc
index 9bafaf0..f6957e4 100644
--- a/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_struct_member_attribute_test.cc
@@ -110,13 +110,39 @@
               ast::IntLiteralExpression::Suffix::kNone);
 }
 
+TEST_F(ParserImplTest, Attribute_Align_Expression) {
+    auto p = parser("align(4 + 5)");
+    auto attr = p->attribute();
+    EXPECT_TRUE(attr.matched);
+    EXPECT_FALSE(attr.errored);
+    ASSERT_FALSE(p->has_error()) << p->error();
+    ASSERT_NE(attr.value, nullptr);
+
+    auto* member_attr = attr.value->As<ast::Attribute>();
+    ASSERT_NE(member_attr, nullptr);
+    ASSERT_TRUE(member_attr->Is<ast::StructMemberAlignAttribute>());
+
+    auto* o = member_attr->As<ast::StructMemberAlignAttribute>();
+    ASSERT_TRUE(o->expr->Is<ast::BinaryExpression>());
+    auto* expr = o->expr->As<ast::BinaryExpression>();
+
+    EXPECT_EQ(ast::BinaryOp::kAdd, expr->op);
+    auto* v = expr->lhs->As<ast::IntLiteralExpression>();
+    ASSERT_NE(nullptr, v);
+    EXPECT_EQ(v->value, 4u);
+
+    v = expr->rhs->As<ast::IntLiteralExpression>();
+    ASSERT_NE(nullptr, v);
+    EXPECT_EQ(v->value, 5u);
+}
+
 TEST_F(ParserImplTest, Attribute_Align_TrailingComma) {
     auto p = parser("align(4,)");
     auto attr = p->attribute();
     EXPECT_TRUE(attr.matched);
     EXPECT_FALSE(attr.errored);
     ASSERT_NE(attr.value, nullptr);
-    ASSERT_FALSE(p->has_error());
+    ASSERT_FALSE(p->has_error()) << p->error();
 
     auto* member_attr = attr.value->As<ast::Attribute>();
     ASSERT_NE(member_attr, nullptr);
@@ -157,17 +183,18 @@
     EXPECT_TRUE(attr.errored);
     EXPECT_EQ(attr.value, nullptr);
     EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:7: expected signed integer literal for align attribute");
+    EXPECT_EQ(p->error(), "1:7: expected align expression");
 }
 
-TEST_F(ParserImplTest, Attribute_Align_MissingInvalid) {
-    auto p = parser("align(nan)");
+TEST_F(ParserImplTest, Attribute_Align_ExpressionInvalid) {
+    auto p = parser("align(4 + 5 << 6)");
     auto attr = p->attribute();
     EXPECT_FALSE(attr.matched);
     EXPECT_TRUE(attr.errored);
     EXPECT_EQ(attr.value, nullptr);
     EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:7: expected signed integer literal for align attribute");
+
+    EXPECT_EQ(p->error(), "1:13: expected ')' for align attribute");
 }
 
 }  // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index 926f7c7..0e018e0 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -24,7 +24,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Invalid) {
     auto p = parser("1234");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_EQ(t.errored, false);
     EXPECT_EQ(t.matched, false);
     EXPECT_EQ(t.value, nullptr);
@@ -34,7 +34,7 @@
 TEST_F(ParserImplTest, TypeDecl_Identifier) {
     auto p = parser("A");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -47,7 +47,7 @@
 TEST_F(ParserImplTest, TypeDecl_Bool) {
     auto p = parser("bool");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -58,7 +58,7 @@
 TEST_F(ParserImplTest, TypeDecl_F16) {
     auto p = parser("f16");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -69,7 +69,7 @@
 TEST_F(ParserImplTest, TypeDecl_F32) {
     auto p = parser("f32");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -80,7 +80,7 @@
 TEST_F(ParserImplTest, TypeDecl_I32) {
     auto p = parser("i32");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -91,7 +91,7 @@
 TEST_F(ParserImplTest, TypeDecl_U32) {
     auto p = parser("u32");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -114,7 +114,7 @@
 TEST_P(VecTest, Parse) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -134,7 +134,7 @@
 TEST_P(VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -152,7 +152,7 @@
 TEST_P(VecMissingType, Handles_Missing_Type) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -167,7 +167,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr) {
     auto p = parser("ptr<function, f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -176,13 +176,13 @@
 
     auto* ptr = t.value->As<ast::Pointer>();
     ASSERT_TRUE(ptr->type->Is<ast::F32>());
-    ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+    ASSERT_EQ(ptr->address_space, ast::AddressSpace::kFunction);
     EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_WithAccess) {
     auto p = parser("ptr<function, f32, read>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -191,14 +191,14 @@
 
     auto* ptr = t.value->As<ast::Pointer>();
     ASSERT_TRUE(ptr->type->Is<ast::F32>());
-    ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+    ASSERT_EQ(ptr->address_space, ast::AddressSpace::kFunction);
     ASSERT_EQ(ptr->access, ast::Access::kRead);
     EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 25u}}));
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
     auto p = parser("ptr<function, vec2<f32>>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -207,7 +207,7 @@
 
     auto* ptr = t.value->As<ast::Pointer>();
     ASSERT_TRUE(ptr->type->Is<ast::Vector>());
-    ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+    ASSERT_EQ(ptr->address_space, ast::AddressSpace::kFunction);
 
     auto* vec = ptr->type->As<ast::Vector>();
     ASSERT_EQ(vec->width, 2u);
@@ -217,7 +217,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingLessThan) {
     auto p = parser("ptr private, f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -227,7 +227,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingGreaterThanAfterType) {
     auto p = parser("ptr<function, f32");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -237,7 +237,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingGreaterThanAfterAccess) {
     auto p = parser("ptr<function, f32, read");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -245,9 +245,9 @@
     ASSERT_EQ(p->error(), "1:24: expected '>' for ptr declaration");
 }
 
-TEST_F(ParserImplTest, TypeDecl_Ptr_MissingCommaAfterStorageClass) {
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingCommaAfterAddressSpace) {
     auto p = parser("ptr<function f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -257,7 +257,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingCommaAfterAccess) {
     auto p = parser("ptr<function, f32 read>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -265,19 +265,19 @@
     ASSERT_EQ(p->error(), "1:19: expected '>' for ptr declaration");
 }
 
-TEST_F(ParserImplTest, TypeDecl_Ptr_MissingStorageClass) {
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingAddressSpace) {
     auto p = parser("ptr<, f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
     ASSERT_TRUE(p->has_error());
-    ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
+    ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingType) {
     auto p = parser("ptr<function,>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -287,7 +287,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingAccess) {
     auto p = parser("ptr<function, i32, >");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -297,27 +297,27 @@
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_MissingParams) {
     auto p = parser("ptr<>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
     ASSERT_TRUE(p->has_error());
-    ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
+    ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
 }
 
-TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) {
+TEST_F(ParserImplTest, TypeDecl_Ptr_BadAddressSpace) {
     auto p = parser("ptr<unknown, f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
     ASSERT_TRUE(p->has_error());
-    ASSERT_EQ(p->error(), "1:5: invalid storage class for ptr declaration");
+    ASSERT_EQ(p->error(), "1:5: invalid address space for ptr declaration");
 }
 
 TEST_F(ParserImplTest, TypeDecl_Ptr_BadAccess) {
     auto p = parser("ptr<function, i32, unknown>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -327,7 +327,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Atomic) {
     auto p = parser("atomic<f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -341,7 +341,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Atomic_ToVec) {
     auto p = parser("atomic<vec2<f32>>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -359,7 +359,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Atomic_MissingLessThan) {
     auto p = parser("atomic f32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -369,7 +369,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Atomic_MissingGreaterThan) {
     auto p = parser("atomic<f32");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -379,7 +379,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Atomic_MissingType) {
     auto p = parser("atomic<>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -389,7 +389,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_AbstractIntLiteralSize) {
     auto p = parser("array<f32, 5>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -410,7 +410,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_SintLiteralSize) {
     auto p = parser("array<f32, 5i>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -431,7 +431,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_UintLiteralSize) {
     auto p = parser("array<f32, 5u>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -451,7 +451,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_ConstantSize) {
     auto p = parser("array<f32, size>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -471,7 +471,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_ExpressionSize) {
     auto p = parser("array<f32, size + 2>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -498,7 +498,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime) {
     auto p = parser("array<u32>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -513,7 +513,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_Runtime_Vec) {
     auto p = parser("array<vec4<u32>>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -530,7 +530,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_BadSize) {
     auto p = parser("array<f32, !>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -540,7 +540,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MissingSize) {
     auto p = parser("array<f32,>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -550,7 +550,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MissingGreaterThan) {
     auto p = parser("array<f32");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -560,7 +560,7 @@
 
 TEST_F(ParserImplTest, TypeDecl_Array_MissingComma) {
     auto p = parser("array<f32 3>");
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -584,7 +584,7 @@
 TEST_P(MatrixTest, Parse) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -612,7 +612,7 @@
 TEST_P(MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -636,7 +636,7 @@
 TEST_P(MatrixMissingType, Handles_Missing_Type) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -658,7 +658,7 @@
 TEST_F(ParserImplTest, TypeDecl_Sampler) {
     auto p = parser("sampler");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -670,7 +670,7 @@
 TEST_F(ParserImplTest, TypeDecl_Texture) {
     auto p = parser("texture_cube<f32>");
 
-    auto t = p->type_decl();
+    auto t = p->type_specifier();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr);
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
index ec23f2f..186b364 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
@@ -24,7 +24,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Invalid) {
     auto p = parser("1234");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_EQ(t.errored, false);
     EXPECT_EQ(t.matched, false);
     EXPECT_EQ(t.value, nullptr);
@@ -33,7 +33,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Identifier) {
     auto p = parser("A");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_FALSE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_EQ(t.value, nullptr);
@@ -42,7 +42,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Bool) {
     auto p = parser("bool");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -52,7 +52,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_F16) {
     auto p = parser("f16");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -62,7 +62,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_F32) {
     auto p = parser("f32");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -72,7 +72,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_I32) {
     auto p = parser("i32");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -82,7 +82,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_U32) {
     auto p = parser("u32");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -105,7 +105,7 @@
 TEST_P(TypeDeclWithoutIdent_VecTest, Parse) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -125,7 +125,7 @@
 TEST_P(TypeDeclWithoutIdent_VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -143,7 +143,7 @@
 TEST_P(TypeDeclWithoutIdent_VecMissingType, Handles_Missing_Type) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -158,7 +158,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr) {
     auto p = parser("ptr<function, f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -167,13 +167,13 @@
 
     auto* ptr = t.value->As<ast::Pointer>();
     ASSERT_TRUE(ptr->type->Is<ast::F32>());
-    ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+    ASSERT_EQ(ptr->address_space, ast::AddressSpace::kFunction);
     EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 19u}}));
 }
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_WithAccess) {
     auto p = parser("ptr<function, f32, read>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -182,14 +182,14 @@
 
     auto* ptr = t.value->As<ast::Pointer>();
     ASSERT_TRUE(ptr->type->Is<ast::F32>());
-    ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+    ASSERT_EQ(ptr->address_space, ast::AddressSpace::kFunction);
     ASSERT_EQ(ptr->access, ast::Access::kRead);
     EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 25u}}));
 }
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_ToVec) {
     auto p = parser("ptr<function, vec2<f32>>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -198,7 +198,7 @@
 
     auto* ptr = t.value->As<ast::Pointer>();
     ASSERT_TRUE(ptr->type->Is<ast::Vector>());
-    ASSERT_EQ(ptr->storage_class, ast::StorageClass::kFunction);
+    ASSERT_EQ(ptr->address_space, ast::AddressSpace::kFunction);
 
     auto* vec = ptr->type->As<ast::Vector>();
     ASSERT_EQ(vec->width, 2u);
@@ -208,7 +208,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingLessThan) {
     auto p = parser("ptr private, f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -218,7 +218,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingGreaterThanAfterType) {
     auto p = parser("ptr<function, f32");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -228,7 +228,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingGreaterThanAfterAccess) {
     auto p = parser("ptr<function, f32, read");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -236,9 +236,9 @@
     ASSERT_EQ(p->error(), "1:24: expected '>' for ptr declaration");
 }
 
-TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingCommaAfterStorageClass) {
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingCommaAfterAddressSpace) {
     auto p = parser("ptr<function f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -248,7 +248,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingCommaAfterAccess) {
     auto p = parser("ptr<function, f32 read>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -256,19 +256,19 @@
     ASSERT_EQ(p->error(), "1:19: expected '>' for ptr declaration");
 }
 
-TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingStorageClass) {
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingAddressSpace) {
     auto p = parser("ptr<, f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
     ASSERT_TRUE(p->has_error());
-    ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
+    ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
 }
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingType) {
     auto p = parser("ptr<function,>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -278,7 +278,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingAccess) {
     auto p = parser("ptr<function, i32, >");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -288,27 +288,27 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingParams) {
     auto p = parser("ptr<>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
     ASSERT_TRUE(p->has_error());
-    ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
+    ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
 }
 
-TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadStorageClass) {
+TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadAddressSpace) {
     auto p = parser("ptr<unknown, f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
     ASSERT_TRUE(p->has_error());
-    ASSERT_EQ(p->error(), "1:5: invalid storage class for ptr declaration");
+    ASSERT_EQ(p->error(), "1:5: invalid address space for ptr declaration");
 }
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadAccess) {
     auto p = parser("ptr<function, i32, unknown>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -318,7 +318,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic) {
     auto p = parser("atomic<f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -332,7 +332,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_ToVec) {
     auto p = parser("atomic<vec2<f32>>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -350,7 +350,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingLessThan) {
     auto p = parser("atomic f32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -360,7 +360,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingGreaterThan) {
     auto p = parser("atomic<f32");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -370,7 +370,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingType) {
     auto p = parser("atomic<>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -380,7 +380,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_AbstractIntLiteralSize) {
     auto p = parser("array<f32, 5>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -401,7 +401,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_SintLiteralSize) {
     auto p = parser("array<f32, 5i>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -422,7 +422,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_UintLiteralSize) {
     auto p = parser("array<f32, 5u>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -442,7 +442,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_ConstantSize) {
     auto p = parser("array<f32, size>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -462,7 +462,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_ExpressionSize) {
     auto p = parser("array<f32, size + 2>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -489,7 +489,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_Runtime) {
     auto p = parser("array<u32>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -504,7 +504,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_Runtime_Vec) {
     auto p = parser("array<vec4<u32>>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -521,7 +521,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_BadSize) {
     auto p = parser("array<f32, !>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -531,7 +531,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingSize) {
     auto p = parser("array<f32,>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -541,7 +541,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingGreaterThan) {
     auto p = parser("array<f32");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -551,7 +551,7 @@
 
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingComma) {
     auto p = parser("array<f32 3>");
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -575,7 +575,7 @@
 TEST_P(TypeDeclWithoutIdent_MatrixTest, Parse) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -604,7 +604,7 @@
 TEST_P(TypeDeclWithoutIdent_MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -628,7 +628,7 @@
 TEST_P(TypeDeclWithoutIdent_MatrixMissingType, Handles_Missing_Type) {
     auto params = GetParam();
     auto p = parser(params.input);
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.errored);
     EXPECT_FALSE(t.matched);
     ASSERT_EQ(t.value, nullptr);
@@ -650,7 +650,7 @@
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Sampler) {
     auto p = parser("sampler");
 
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr) << p->error();
@@ -662,7 +662,7 @@
 TEST_F(ParserImplTest, TypeDeclWithoutIdent_Texture) {
     auto p = parser("texture_cube<f32>");
 
-    auto t = p->type_decl_without_ident();
+    auto t = p->type_specifier_without_ident();
     EXPECT_TRUE(t.matched);
     EXPECT_FALSE(t.errored);
     ASSERT_NE(t.value, nullptr);
diff --git a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
index 2fee83c..8427c6e 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -72,7 +72,7 @@
     ASSERT_TRUE(t.IsIdentifier());
 }
 
-TEST_F(ParserImplTest, VariableDecl_WithStorageClass) {
+TEST_F(ParserImplTest, VariableDecl_WithAddressSpace) {
     auto p = parser("var<private> my_var : f32");
     auto v = p->variable_decl();
     EXPECT_TRUE(v.matched);
@@ -80,7 +80,7 @@
     EXPECT_FALSE(p->has_error());
     EXPECT_EQ(v->name, "my_var");
     EXPECT_TRUE(v->type->Is<ast::F32>());
-    EXPECT_EQ(v->storage_class, ast::StorageClass::kPrivate);
+    EXPECT_EQ(v->address_space, ast::AddressSpace::kPrivate);
 
     EXPECT_EQ(v->source.range.begin.line, 1u);
     EXPECT_EQ(v->source.range.begin.column, 14u);
@@ -96,16 +96,16 @@
     EXPECT_FALSE(p->has_error());
     EXPECT_EQ(v->name, "my_var");
     EXPECT_TRUE(v->type->Is<ast::F32>());
-    EXPECT_EQ(v->storage_class, ast::StorageClass::kPushConstant);
+    EXPECT_EQ(v->address_space, ast::AddressSpace::kPushConstant);
 }
 
-TEST_F(ParserImplTest, VariableDecl_InvalidStorageClass) {
+TEST_F(ParserImplTest, VariableDecl_InvalidAddressSpace) {
     auto p = parser("var<unknown> my_var : f32");
     auto v = p->variable_decl();
     EXPECT_FALSE(v.matched);
     EXPECT_TRUE(v.errored);
     EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:5: invalid storage class for variable declaration");
+    EXPECT_EQ(p->error(), "1:5: invalid address space for variable declaration");
 }
 
 }  // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index 00dd59b..57bcf19 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -19,7 +19,7 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
     auto p = parser("my_var : f32");
-    auto decl = p->expect_ident_with_type_decl("test");
+    auto decl = p->expect_ident_with_type_specifier("test");
     ASSERT_FALSE(p->has_error()) << p->error();
     ASSERT_FALSE(decl.errored);
     ASSERT_EQ(decl->name, "my_var");
@@ -45,7 +45,7 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_Inferred_Parse_Failure) {
     auto p = parser("my_var = 1.0");
-    auto decl = p->expect_ident_with_type_decl("test");
+    auto decl = p->expect_ident_with_type_specifier("test");
     ASSERT_TRUE(p->has_error());
     ASSERT_EQ(p->error(), "1:8: expected ':' for test");
 }
@@ -63,7 +63,7 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
     auto p = parser(": f32");
-    auto decl = p->expect_ident_with_type_decl("test");
+    auto decl = p->expect_ident_with_type_specifier("test");
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
     ASSERT_EQ(p->error(), "1:1: expected identifier for test");
@@ -79,7 +79,7 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingType) {
     auto p = parser("my_var :");
-    auto decl = p->expect_ident_with_type_decl("test");
+    auto decl = p->expect_ident_with_type_specifier("test");
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
     ASSERT_EQ(p->error(), "1:9: invalid type for test");
@@ -95,7 +95,7 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_InvalidIdent) {
     auto p = parser("123 : f32");
-    auto decl = p->expect_ident_with_type_decl("test");
+    auto decl = p->expect_ident_with_type_specifier("test");
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
     ASSERT_EQ(p->error(), "1:1: expected identifier for test");
diff --git a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
index 5362d38..9f8663d 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
@@ -19,7 +19,7 @@
 
 struct VariableStorageData {
     const char* input;
-    ast::StorageClass storage_class;
+    ast::AddressSpace address_space;
     ast::Access access;
 };
 inline std::ostream& operator<<(std::ostream& out, VariableStorageData data) {
@@ -29,7 +29,7 @@
 
 class VariableQualifierTest : public ParserImplTestWithParam<VariableStorageData> {};
 
-TEST_P(VariableQualifierTest, ParsesStorageClass) {
+TEST_P(VariableQualifierTest, ParsesAddressSpace) {
     auto params = GetParam();
     auto p = parser(std::string("<") + params.input + ">");
 
@@ -37,7 +37,7 @@
     EXPECT_FALSE(p->has_error());
     EXPECT_FALSE(sc.errored);
     EXPECT_TRUE(sc.matched);
-    EXPECT_EQ(sc->storage_class, params.storage_class);
+    EXPECT_EQ(sc->address_space, params.address_space);
     EXPECT_EQ(sc->access, params.access);
 
     auto& t = p->next();
@@ -47,14 +47,14 @@
     ParserImplTest,
     VariableQualifierTest,
     testing::Values(
-        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{"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,
+        VariableStorageData{"uniform", ast::AddressSpace::kUniform, ast::Access::kUndefined},
+        VariableStorageData{"workgroup", ast::AddressSpace::kWorkgroup, ast::Access::kUndefined},
+        VariableStorageData{"storage", ast::AddressSpace::kStorage, ast::Access::kUndefined},
+        VariableStorageData{"private", ast::AddressSpace::kPrivate, ast::Access::kUndefined},
+        VariableStorageData{"function", ast::AddressSpace::kFunction, ast::Access::kUndefined},
+        VariableStorageData{"storage, read", ast::AddressSpace::kStorage, ast::Access::kRead},
+        VariableStorageData{"storage, write", ast::AddressSpace::kStorage, ast::Access::kWrite},
+        VariableStorageData{"storage, read_write", ast::AddressSpace::kStorage,
                             ast::Access::kReadWrite}));
 
 TEST_F(ParserImplTest, VariableQualifier_NoMatch) {
@@ -63,7 +63,7 @@
     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");
+    EXPECT_EQ(p->error(), "1:2: invalid address space for variable declaration");
 }
 
 TEST_F(ParserImplTest, VariableQualifier_Empty) {
@@ -72,7 +72,7 @@
     EXPECT_TRUE(p->has_error());
     EXPECT_TRUE(sc.errored);
     EXPECT_FALSE(sc.matched);
-    EXPECT_EQ(p->error(), "1:2: expected identifier for storage class");
+    EXPECT_EQ(p->error(), "1:2: expected identifier for address space");
 }
 
 TEST_F(ParserImplTest, VariableQualifier_MissingLessThan) {
diff --git a/src/tint/resolver/storage_class_layout_validation_test.cc b/src/tint/resolver/address_space_layout_validation_test.cc
similarity index 87%
rename from src/tint/resolver/storage_class_layout_validation_test.cc
rename to src/tint/resolver/address_space_layout_validation_test.cc
index 91d0d5d..82da573 100644
--- a/src/tint/resolver/storage_class_layout_validation_test.cc
+++ b/src/tint/resolver/address_space_layout_validation_test.cc
@@ -22,10 +22,10 @@
 namespace tint::resolver {
 namespace {
 
-using ResolverStorageClassLayoutValidationTest = ResolverTest;
+using ResolverAddressSpaceLayoutValidationTest = ResolverTest;
 
 // Detect unaligned member for storage buffers
-TEST_F(ResolverStorageClassLayoutValidationTest, StorageBuffer_UnalignedMember) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, StorageBuffer_UnalignedMember) {
     // struct S {
     //     @size(5) a : f32;
     //     @align(1) b : f32;
@@ -36,16 +36,16 @@
     Structure(Source{{12, 34}}, "S",
               utils::Vector{
                   Member("a", ty.f32(), utils::Vector{MemberSize(5_a)}),
-                  Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(1_u)}),
+                  Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(1_i)}),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::AddressSpace::kStorage, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(34:56 error: the offset of a struct member of type 'f32' in storage class 'storage' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting @align(4) on this member
+        R"(34:56 error: the offset of a struct member of type 'f32' in address space 'storage' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting @align(4) on this member
 12:34 note: see layout of struct:
 /*           align(4) size(12) */ struct S {
 /* offset(0) align(4) size( 5) */   a : f32;
@@ -55,7 +55,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, StorageBuffer_UnalignedMember_SuggestedFix) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, StorageBuffer_UnalignedMember_SuggestedFix) {
     // struct S {
     //     @size(5) a : f32;
     //     @align(4) b : f32;
@@ -66,17 +66,17 @@
     Structure(Source{{12, 34}}, "S",
               utils::Vector{
                   Member("a", ty.f32(), utils::Vector{MemberSize(5_a)}),
-                  Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(4_u)}),
+                  Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(4_i)}),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::AddressSpace::kStorage, Group(0_a),
               Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 // Detect unaligned struct member for uniform buffers
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_UnalignedMember_Struct) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_Struct) {
     // struct Inner {
     //   scalar : i32;
     // };
@@ -100,13 +100,13 @@
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: the offset of a struct member of type 'Inner' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
+        R"(56:78 error: the offset of a struct member of type 'Inner' in address space 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
 34:56 note: see layout of struct:
 /*           align(4) size(8) */ struct Outer {
 /* offset(0) align(4) size(4) */   scalar : f32;
@@ -119,7 +119,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest,
+TEST_F(ResolverAddressSpaceLayoutValidationTest,
        UniformBuffer_UnalignedMember_Struct_SuggestedFix) {
     // struct Inner {
     //   scalar : i32;
@@ -142,17 +142,17 @@
               utils::Vector{
                   Member("scalar", ty.f32()),
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner"),
-                         utils::Vector{MemberAlign(16_u)}),
+                         utils::Vector{MemberAlign(16_i)}),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 // Detect unaligned array member for uniform buffers
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_UnalignedMember_Array) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_Array) {
     // type Inner = @stride(16) array<f32, 10u>;
     //
     // struct Outer {
@@ -170,13 +170,13 @@
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: the offset of a struct member of type '@stride(16) array<f32, 10>' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
+        R"(56:78 error: the offset of a struct member of type '@stride(16) array<f32, 10>' in address space 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
 12:34 note: see layout of struct:
 /*             align(4) size(164) */ struct Outer {
 /* offset(  0) align(4) size(  4) */   scalar : f32;
@@ -185,7 +185,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_UnalignedMember_Array_SuggestedFix) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_Array_SuggestedFix) {
     // type Inner = @stride(16) array<f32, 10u>;
     //
     // struct Outer {
@@ -201,10 +201,10 @@
               utils::Vector{
                   Member("scalar", ty.f32()),
                   Member(Source{{34, 56}}, "inner", ty.type_name("Inner"),
-                         utils::Vector{MemberAlign(16_u)}),
+                         utils::Vector{MemberAlign(16_i)}),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -212,7 +212,7 @@
 
 // Detect uniform buffers with byte offset between 2 members that is not a
 // multiple of 16 bytes
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_MembersOffsetNotMultipleOf16) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_MembersOffsetNotMultipleOf16) {
     // struct Inner {
     //   @align(1) @size(5) scalar : i32;
     // };
@@ -227,7 +227,7 @@
 
     Structure(Source{{12, 34}}, "Inner",
               utils::Vector{
-                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1_u), MemberSize(5_a)}),
+                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1_i), MemberSize(5_a)}),
               });
 
     Structure(Source{{34, 56}}, "Outer",
@@ -236,7 +236,7 @@
                   Member(Source{{78, 90}}, "scalar", ty.i32()),
               });
 
-    GlobalVar(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -257,7 +257,7 @@
 }
 
 // See https://crbug.com/tint/1344
-TEST_F(ResolverStorageClassLayoutValidationTest,
+TEST_F(ResolverAddressSpaceLayoutValidationTest,
        UniformBuffer_MembersOffsetNotMultipleOf16_InnerMoreMembersThanOuter) {
     // struct Inner {
     //   a : i32;
@@ -279,7 +279,7 @@
                   Member("a", ty.i32()),
                   Member("b", ty.i32()),
                   Member("c", ty.i32()),
-                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1_u), MemberSize(5_a)}),
+                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1_i), MemberSize(5_a)}),
               });
 
     Structure(Source{{34, 56}}, "Outer",
@@ -288,7 +288,7 @@
                   Member(Source{{78, 90}}, "scalar", ty.i32()),
               });
 
-    GlobalVar(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -311,7 +311,7 @@
 22:24 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest,
+TEST_F(ResolverAddressSpaceLayoutValidationTest,
        UniformBuffer_MembersOffsetNotMultipleOf16_SuggestedFix) {
     // struct Inner {
     //   @align(1) @size(5) scalar : i32;
@@ -327,16 +327,16 @@
 
     Structure(Source{{12, 34}}, "Inner",
               utils::Vector{
-                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1_u), MemberSize(5_a)}),
+                  Member("scalar", ty.i32(), utils::Vector{MemberAlign(1_i), MemberSize(5_a)}),
               });
 
     Structure(Source{{34, 56}}, "Outer",
               utils::Vector{
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
-                  Member(Source{{78, 90}}, "scalar", ty.i32(), utils::Vector{MemberAlign(16_u)}),
+                  Member(Source{{78, 90}}, "scalar", ty.i32(), utils::Vector{MemberAlign(16_i)}),
               });
 
-    GlobalVar(Source{{22, 34}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{22, 34}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -344,7 +344,7 @@
 
 // Make sure that this doesn't fail validation because vec3's align is 16, but
 // size is 12. 's' should be at offset 12, which is okay here.
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_Vec3MemberOffset_NoFail) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_Vec3MemberOffset_NoFail) {
     // struct ScalarPackedAtEndOfVec3 {
     //     v : vec3<f32>;
     //     s : f32;
@@ -358,13 +358,13 @@
                                          });
 
     GlobalVar(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
-              ast::StorageClass::kUniform, Group(0_a), Binding(0_a));
+              ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 // Detect array stride must be a multiple of 16 bytes for uniform buffers
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_Scalar) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Scalar) {
     // type Inner = array<f32, 10u>;
     //
     // struct Outer {
@@ -383,7 +383,7 @@
                   Member("scalar", ty.i32()),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -398,7 +398,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_Vector) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Vector) {
     // type Inner = array<vec2<f32>, 10u>;
     //
     // struct Outer {
@@ -417,7 +417,7 @@
                   Member("scalar", ty.i32()),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -433,7 +433,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_Struct) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Struct) {
     // struct ArrayElem {
     //   a : f32;
     //   b : i32;
@@ -460,7 +460,7 @@
                   Member("scalar", ty.i32()),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -475,11 +475,11 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
     // @group(0) @binding(0)
     // var<uniform> a : array<f32, 4u>;
     GlobalVar(Source{{78, 90}}, "a", ty.array(Source{{34, 56}}, ty.f32(), 4_u),
-              ast::StorageClass::kUniform, Group(0_a), Binding(0_a));
+              ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -487,7 +487,7 @@
         R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
     // struct Outer {
     //   inner : array<array<f32, 4u>, 4u>
     // };
@@ -500,7 +500,7 @@
                   Member("inner", ty.array(Source{{34, 56}}, ty.array(ty.f32(), 4_u), 4_u)),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -514,7 +514,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_SuggestedFix) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_SuggestedFix) {
     // type Inner = @stride(16) array<f32, 10u>;
     //
     // struct Outer {
@@ -533,14 +533,14 @@
                   Member("scalar", ty.i32()),
               });
 
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform, Group(0_a),
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::AddressSpace::kUniform, Group(0_a),
               Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 // Detect unaligned member for push constants buffers
-TEST_F(ResolverStorageClassLayoutValidationTest, PushConstant_UnalignedMember) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, PushConstant_UnalignedMember) {
     // enable chromium_experimental_push_constant;
     // struct S {
     //     @size(5) a : f32;
@@ -551,13 +551,13 @@
     Structure(
         Source{{12, 34}}, "S",
         utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(5_a)}),
-                      Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(1_u)})});
-    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kPushConstant);
+                      Member(Source{{34, 56}}, "b", ty.f32(), utils::Vector{MemberAlign(1_i)})});
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::AddressSpace::kPushConstant);
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(34:56 error: the offset of a struct member of type 'f32' in storage class 'push_constant' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting @align(4) on this member
+        R"(34:56 error: the offset of a struct member of type 'f32' in address space 'push_constant' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting @align(4) on this member
 12:34 note: see layout of struct:
 /*           align(4) size(12) */ struct S {
 /* offset(0) align(4) size( 5) */   a : f32;
@@ -567,7 +567,7 @@
 78:90 note: see declaration of variable)");
 }
 
-TEST_F(ResolverStorageClassLayoutValidationTest, PushConstant_Aligned) {
+TEST_F(ResolverAddressSpaceLayoutValidationTest, PushConstant_Aligned) {
     // enable chromium_experimental_push_constant;
     // struct S {
     //     @size(5) a : f32;
@@ -576,8 +576,8 @@
     // var<push_constant> a : S;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
     Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(5_a)}),
-                                 Member("b", ty.f32(), utils::Vector{MemberAlign(4_u)})});
-    GlobalVar("a", ty.type_name("S"), ast::StorageClass::kPushConstant);
+                                 Member("b", ty.f32(), utils::Vector{MemberAlign(4_i)})});
+    GlobalVar("a", ty.type_name("S"), ast::AddressSpace::kPushConstant);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/address_space_validation_test.cc
similarity index 61%
rename from src/tint/resolver/storage_class_validation_test.cc
rename to src/tint/resolver/address_space_validation_test.cc
index cff20c0..ae68013 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/address_space_validation_test.cc
@@ -25,169 +25,169 @@
 
 using ::testing::HasSubstr;
 
-using ResolverStorageClassValidationTest = ResolverTest;
+using ResolverAddressSpaceValidationTest = ResolverTest;
 
-TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariableNoAddressSpace_Fail) {
     // var g : f32;
     GlobalVar(Source{{12, 34}}, "g", ty.f32());
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: module-scope 'var' declaration must have a storage class");
+              "12:34 error: module-scope 'var' declaration must have a address space");
 }
 
-TEST_F(ResolverStorageClassValidationTest, GlobalVariableFunctionStorageClass_Fail) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariableFunctionAddressSpace_Fail) {
     // var<function> g : f32;
-    GlobalVar(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kFunction);
+    GlobalVar(Source{{12, 34}}, "g", ty.f32(), ast::AddressSpace::kFunction);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: module-scope 'var' must not use storage class 'function'");
+              "12:34 error: module-scope 'var' must not use address space 'function'");
 }
 
-TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArray) {
-    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kPrivate);
+TEST_F(ResolverAddressSpaceValidationTest, Private_RuntimeArray) {
+    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 12:34 note: while instantiating 'var' v)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArrayInStruct) {
+TEST_F(ResolverAddressSpaceValidationTest, Private_RuntimeArrayInStruct) {
     auto* s = Structure("S", utils::Vector{Member("m", ty.array(ty.i32()))});
-    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 note: while analysing structure member S.m
 12:34 note: while instantiating 'var' v)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArray) {
-    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kWorkgroup);
+TEST_F(ResolverAddressSpaceValidationTest, Workgroup_RuntimeArray) {
+    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::AddressSpace::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 12:34 note: while instantiating 'var' v)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArrayInStruct) {
+TEST_F(ResolverAddressSpaceValidationTest, Workgroup_RuntimeArrayInStruct) {
     auto* s = Structure("S", utils::Vector{Member("m", ty.array(ty.i32()))});
-    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::AddressSpace::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 note: while analysing structure member S.m
 12:34 note: while instantiating 'var' v)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferBool) {
     // var<storage> g : bool;
-    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kStorage, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferBoolAlias) {
     // type a = bool;
     // var<storage, read> g : a;
     auto* a = Alias("a", ty.bool_());
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::AddressSpace::kStorage, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
 // F16 types in storage and uniform buffer is not implemented yet.
 // TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
-TEST_F(ResolverStorageClassValidationTest, StorageBufferF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16_TemporallyBan) {
     // var<storage> g : f16;
     Enable(ast::Extension::kF16);
 
-    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kStorage, Binding(0_a), Group(0_a));
+    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'storage' storage class is not "
+              "56:78 error: using f16 types in 'storage' address space is not "
               "implemented yet");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferF16Alias_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16Alias_TemporallyBan) {
     // type a = f16;
     // var<storage, read> g : a;
     Enable(ast::Extension::kF16);
 
     auto* a = Alias("a", ty.f16());
-    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kStorage,
+    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::AddressSpace::kStorage,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'storage' storage class is not "
+              "56:78 error: using f16 types in 'storage' address space is not "
               "implemented yet");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF16_TemporallyBan) {
     // var<storage> g : vec4<f16>;
     Enable(ast::Extension::kF16);
-    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kStorage,
+    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kStorage,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'storage' storage class is not "
+              "56:78 error: using f16 types in 'storage' address space is not "
               "implemented yet");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF16_TemporallyBan) {
     // struct S { a : f16 };
     // var<storage, read> g : array<S, 3u>;
     Enable(ast::Extension::kF16);
 
     auto* s = Structure("S", utils::Vector{Member("a", ty.f16(Source{{56, 78}}))});
     auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar("g", a, ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a), Group(0_a));
+    GlobalVar("g", a, ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'storage' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'storage' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferStructF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16_TemporallyBan) {
     // struct S { x : f16 };
     // var<storage, read> g : S;
     Enable(ast::Extension::kF16);
 
     auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'storage' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'storage' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferNoErrorStructF16Aliases_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferNoErrorStructF16Aliases_TemporallyBan) {
     // struct S { x : f16 };
     // type a1 = S;
     // var<storage, read> g : a1;
@@ -196,84 +196,84 @@
     auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
     auto* a1 = Alias("a1", ty.Of(s));
     auto* a2 = Alias("a2", ty.Of(a1));
-    GlobalVar("g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'storage' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'storage' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferPointer) {
     // var<storage> g : ptr<private, f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
-              ast::StorageClass::kStorage, Binding(0_a), Group(0_a));
+    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+              ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'storage' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferIntScalar) {
     // var<storage> g : i32;
-    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kStorage, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF32) {
     // var<storage> g : vec4<f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kStorage, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF32) {
     // var<storage, read> g : array<S, 3u>;
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, NotStorage_AccessMode) {
     // var<private, read> g : a;
-    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate, ast::Access::kRead);
+    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead);
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: only variables in <storage> storage class may declare an access mode)");
+        R"(56:78 error: only variables in <storage> address space may declare an access mode)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, Storage_ReadAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, Storage_ReadAccessMode) {
     // @group(0) @binding(0) var<storage, read> a : i32;
-    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, Storage_ReadWriteAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, Storage_ReadWriteAccessMode) {
     // @group(0) @binding(0) var<storage, read_write> a : i32;
-    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
               Group(0_a), Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, Storage_WriteAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, Storage_WriteAccessMode) {
     // @group(0) @binding(0) var<storage, read_write> a : i32;
-    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kWrite,
+    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite,
               Group(0_a), Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
@@ -282,117 +282,117 @@
               R"(56:78 error: access mode 'write' is not valid for the 'storage' address space)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferStructI32) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32) {
     // struct S { x : i32 };
     // var<storage, read> g : S;
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverStorageClassValidationTest, StorageBufferNoErrorStructI32Aliases) {
+TEST_F(ResolverAddressSpaceValidationTest, StorageBufferNoErrorStructI32Aliases) {
     // struct S { x : i32 };
     // type a1 = S;
     // var<storage, read> g : a1;
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     auto* a1 = Alias("a1", ty.Of(s));
     auto* a2 = Alias("a2", ty.Of(a1));
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBuffer_Struct_Runtime) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBuffer_Struct_Runtime) {
     // struct S { m:  array<f32>; };
     // @group(0) @binding(0) var<uniform, > svar : S;
 
     auto* s = Structure(Source{{12, 34}}, "S", utils::Vector{Member("m", ty.array<i32>())});
 
-    GlobalVar(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "svar", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
 note: while analysing structure member S.m
 56:78 note: while instantiating 'var' svar)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferBool) {
     // var<uniform> g : bool;
-    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferBoolAlias) {
     // type a = bool;
     // var<uniform> g : a;
     auto* a = Alias("a", ty.bool_());
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
 // F16 types in storage and uniform buffer is not implemented yet.
 // TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
-TEST_F(ResolverStorageClassValidationTest, UniformBufferF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16_TemporallyBan) {
     // var<uniform> g : f16;
     Enable(ast::Extension::kF16);
 
-    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'uniform' storage class is not "
+              "56:78 error: using f16 types in 'uniform' address space is not "
               "implemented yet");
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferF16Alias_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16Alias_TemporallyBan) {
     // type a = f16;
     // var<uniform> g : a;
     Enable(ast::Extension::kF16);
 
     auto* a = Alias("a", ty.f16());
-    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kUniform,
+    GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::AddressSpace::kUniform,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'uniform' storage class is not "
+              "56:78 error: using f16 types in 'uniform' address space is not "
               "implemented yet");
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF16_TemporallyBan) {
     // var<uniform> g : vec4<f16>;
     Enable(ast::Extension::kF16);
-    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kUniform,
+    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kUniform,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'uniform' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'uniform' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF16_TemporallyBan) {
     // struct S {
     //   @size(16) f : f16;
     // }
@@ -402,29 +402,29 @@
     auto* s = Structure(
         "S", utils::Vector{Member("a", ty.f16(Source{{56, 78}}), utils::Vector{MemberSize(16_a)})});
     auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar("g", a, ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar("g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'uniform' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("56:78 error: using f16 types in 'uniform' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferStructF16_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16_TemporallyBan) {
     // struct S { x : f16 };
     // var<uniform> g :  S;
     Enable(ast::Extension::kF16);
 
     auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'uniform' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'uniform' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferStructF16Aliases_TemporallyBan) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16Aliases_TemporallyBan) {
     // struct S { x : f16 };
     // type a1 = S;
     // var<uniform> g : a1;
@@ -432,159 +432,159 @@
 
     auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
     auto* a1 = Alias("a1", ty.Of(s));
-    GlobalVar("g", ty.Of(a1), ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar("g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
-    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'uniform' storage "
-                                        "class is not implemented yet"));
+    EXPECT_THAT(r()->error(), HasSubstr("12:34 error: using f16 types in 'uniform' address "
+                                        "space is not implemented yet"));
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferPointer) {
     // var<uniform> g : ptr<private, f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
-              ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+              ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'uniform' as it is non-host-shareable
+        R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'uniform' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferIntScalar) {
     // var<uniform> g : i32;
-    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF32) {
     // var<uniform> g : vec4<f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF32) {
     // struct S {
     //   @size(16) f : f32;
     // }
     // var<uniform> g : array<S, 3u>;
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16_a)})});
     auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructI32) {
     // struct S { x : i32 };
     // var<uniform> g :  S;
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32Aliases) {
+TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructI32Aliases) {
     // struct S { x : i32 };
     // type a1 = S;
     // var<uniform> g : a1;
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
     auto* a1 = Alias("a1", ty.Of(s));
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform, Binding(0_a),
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantBool) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantBool) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : bool;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kPushConstant);
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kPushConstant);
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'push_constant' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantF16) {
     // enable chromium_experimental_push_constant;
     // enable f16;
     // var<push_constant> g : f16;
     Enable(ast::Extension::kF16);
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kPushConstant);
+    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kPushConstant);
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "56:78 error: using f16 types in 'push_constant' storage class is not "
+              "56:78 error: using f16 types in 'push_constant' address space is not "
               "implemented yet");
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantPointer) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantPointer) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : ptr<private, f32>;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
-              ast::StorageClass::kPushConstant);
+    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+              ast::AddressSpace::kPushConstant);
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'push_constant' as it is non-host-shareable
+        R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'push_constant' as it is non-host-shareable
 56:78 note: while instantiating 'var' g)");
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantIntScalar) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : i32;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar("g", ty.i32(), ast::StorageClass::kPushConstant);
+    GlobalVar("g", ty.i32(), ast::AddressSpace::kPushConstant);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantVectorF32) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : vec4<f32>;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPushConstant);
+    GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPushConstant);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantArrayF32) {
     // enable chromium_experimental_push_constant;
     // struct S { a : f32}
     // var<push_constant> g : array<S, 3u>;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar("g", a, ast::StorageClass::kPushConstant);
+    GlobalVar("g", a, ast::AddressSpace::kPushConstant);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverStorageClassValidationTest, PushConstantWithInitializer) {
+TEST_F(ResolverAddressSpaceValidationTest, PushConstantWithInitializer) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> a : u32 = 0u;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{1u, 2u}}, "a", ty.u32(), ast::StorageClass::kPushConstant,
+    GlobalVar(Source{{1u, 2u}}, "a", ty.u32(), ast::AddressSpace::kPushConstant,
               Expr(Source{{3u, 4u}}, u32(0)));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(1:2 error: var of storage class 'push_constant' cannot have an initializer. var initializers are only supported for the storage classes 'private' and 'function')");
+        R"(1:2 error: var of address space 'push_constant' cannot have an initializer. var initializers are only supported for the address spacees 'private' and 'function')");
 }
 
 }  // namespace
diff --git a/src/tint/resolver/array_accessor_test.cc b/src/tint/resolver/array_accessor_test.cc
index f63bb1f..d7327de 100644
--- a/src/tint/resolver/array_accessor_test.cc
+++ b/src/tint/resolver/array_accessor_test.cc
@@ -27,7 +27,7 @@
 using ResolverIndexAccessorTest = ResolverTest;
 
 TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_F32) {
-    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::AddressSpace::kPrivate);
     auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 1_f));
     WrapInFunction(acc);
 
@@ -36,7 +36,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_Ref) {
-    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::AddressSpace::kPrivate);
     auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
     auto* acc = IndexAccessor("my_var", idx);
     WrapInFunction(Decl(idx), acc);
@@ -50,7 +50,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions_Dynamic_Ref) {
-    GlobalVar("my_var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat4x4<f32>(), ast::AddressSpace::kPrivate);
     auto* idx = Var("idx", ty.u32(), Expr(3_u));
     auto* idy = Var("idy", ty.u32(), Expr(2_u));
     auto* acc = IndexAccessor(IndexAccessor("my_var", idx), idy);
@@ -100,7 +100,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix) {
-    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* acc = IndexAccessor("my_var", 1_i);
     WrapInFunction(acc);
@@ -121,7 +121,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions) {
-    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* acc = IndexAccessor(IndexAccessor("my_var", 0_i), 1_i);
     WrapInFunction(acc);
@@ -141,7 +141,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector_F32) {
-    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
     auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 2_f));
     WrapInFunction(acc);
 
@@ -150,7 +150,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector_Dynamic_Ref) {
-    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
     auto* idx = Var("idx", ty.i32(), Expr(2_i));
     auto* acc = IndexAccessor("my_var", idx);
     WrapInFunction(Decl(idx), acc);
@@ -173,7 +173,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector) {
-    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
@@ -193,7 +193,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_i32) {
-    GlobalVar("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.array<f32, 3>(), ast::AddressSpace::kPrivate);
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -209,7 +209,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_u32) {
-    GlobalVar("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.array<f32, 3>(), ast::AddressSpace::kPrivate);
     auto* acc = IndexAccessor("my_var", 2_u);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -225,7 +225,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_AInt) {
-    GlobalVar("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.array<f32, 3>(), ast::AddressSpace::kPrivate);
     auto* acc = IndexAccessor("my_var", 2_a);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -243,7 +243,7 @@
 TEST_F(ResolverIndexAccessorTest, Alias_Array) {
     auto* aary = Alias("myarrty", ty.array<f32, 3>());
 
-    GlobalVar("my_var", ty.Of(aary), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.Of(aary), ast::AddressSpace::kPrivate);
 
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
@@ -337,7 +337,7 @@
     //     let x: f32 = (*p)[idx];
     //     return x;
     // }
-    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
+    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction));
     auto* idx = Let("idx", ty.u32(), Construct(ty.u32()));
     auto* star_p = Deref(p);
     auto* acc = IndexAccessor(Source{{12, 34}}, star_p, idx);
@@ -358,7 +358,7 @@
     //     let x: f32 = *p[idx];
     //     return x;
     // }
-    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
+    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction));
     auto* idx = Let("idx", ty.u32(), Construct(ty.u32()));
     auto* accessor_expr = IndexAccessor(Source{{12, 34}}, p, idx);
     auto* star_p = Deref(accessor_expr);
diff --git a/src/tint/resolver/assignment_validation_test.cc b/src/tint/resolver/assignment_validation_test.cc
index 36ce0cb..02a6191 100644
--- a/src/tint/resolver/assignment_validation_test.cc
+++ b/src/tint/resolver/assignment_validation_test.cc
@@ -32,7 +32,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("m", ty.i32()),
                              });
-    GlobalVar(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{12, 34}}, "a", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("a", "m"), 1_i));
@@ -192,7 +192,7 @@
     // var a : i32;
     // let b : ptr<function,i32> = &a;
     // *b = 2i;
-    const auto func = ast::StorageClass::kFunction;
+    const auto func = ast::AddressSpace::kFunction;
     WrapInFunction(Var("a", ty.i32(), func, Expr(2_i)),                    //
                    Let("b", ty.pointer<i32>(func), AddressOf(Expr("a"))),  //
                    Assign(Deref("b"), 2_i));
@@ -204,7 +204,7 @@
     // var a : i32;
     // let b : ptr<function,i32> = &a;
     // *b = 2;
-    const auto func = ast::StorageClass::kFunction;
+    const auto func = ast::AddressSpace::kFunction;
     auto* var_a = Var("a", ty.i32(), func, Expr(2_i));
     auto* var_b = Let("b", ty.pointer<i32>(func), AddressOf(Expr("a")));
     WrapInFunction(var_a, var_b, Assign(Deref("b"), 2_a));
@@ -251,7 +251,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.atomic(ty.i32())),
                              });
-    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
               Binding(0_a), Group(0_a));
 
     WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"), MemberAccessor("v", "a")));
@@ -268,7 +268,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.array(ty.f32())),
                              });
-    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
               Binding(0_a), Group(0_a));
 
     WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"), MemberAccessor("v", "a")));
@@ -288,7 +288,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("arr", ty.array<i32>()),
                              });
-    GlobalVar("s", ty.Of(s), ast::StorageClass::kStorage, Group(0_a), Binding(0_a));
+    GlobalVar("s", ty.Of(s), ast::AddressSpace::kStorage, Group(0_a), Binding(0_a));
 
     WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
 
@@ -310,7 +310,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("arr", ty.array<i32>()),
                              });
-    GlobalVar("s", ty.Of(s), ast::StorageClass::kStorage, Group(0_a), Binding(0_a));
+    GlobalVar("s", ty.Of(s), ast::AddressSpace::kStorage, Group(0_a), Binding(0_a));
 
     WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
 
@@ -363,9 +363,9 @@
     GlobalVar("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), Group(0_a),
               Binding(0_a));
     GlobalVar("smp", ty.sampler(ast::SamplerKind::kSampler), Group(0_a), Binding(1_a));
-    GlobalVar("u", ty.Of(U), ast::StorageClass::kUniform, Group(0_a), Binding(2_a));
-    GlobalVar("s", ty.Of(S), ast::StorageClass::kStorage, Group(0_a), Binding(3_a));
-    GlobalVar("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("u", ty.Of(U), ast::AddressSpace::kUniform, Group(0_a), Binding(2_a));
+    GlobalVar("s", ty.Of(S), ast::AddressSpace::kStorage, Group(0_a), Binding(3_a));
+    GlobalVar("wg", ty.array<f32, 10>(), ast::AddressSpace::kWorkgroup);
 
     WrapInFunction(Assign(Phony(), 1_i),                                    //
                    Assign(Phony(), 2_u),                                    //
diff --git a/src/tint/resolver/atomics_test.cc b/src/tint/resolver/atomics_test.cc
index a22977e..bb08e83 100644
--- a/src/tint/resolver/atomics_test.cc
+++ b/src/tint/resolver/atomics_test.cc
@@ -27,7 +27,7 @@
 struct ResolverAtomicTest : public resolver::TestHelper, public testing::Test {};
 
 TEST_F(ResolverAtomicTest, GlobalWorkgroupI32) {
-    auto* g = GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
+    auto* g = GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
@@ -37,7 +37,7 @@
 }
 
 TEST_F(ResolverAtomicTest, GlobalWorkgroupU32) {
-    auto* g = GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.u32()), ast::StorageClass::kWorkgroup);
+    auto* g = GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.u32()), ast::AddressSpace::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
@@ -48,7 +48,7 @@
 
 TEST_F(ResolverAtomicTest, GlobalStorageStruct) {
     auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    auto* g = GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    auto* g = GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
                         Binding(0_a), Group(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/atomics_validation_test.cc b/src/tint/resolver/atomics_validation_test.cc
index aa4c1c2..08bdf13 100644
--- a/src/tint/resolver/atomics_validation_test.cc
+++ b/src/tint/resolver/atomics_validation_test.cc
@@ -26,64 +26,64 @@
 
 struct ResolverAtomicValidationTest : public resolver::TestHelper, public testing::Test {};
 
-TEST_F(ResolverAtomicValidationTest, StorageClass_WorkGroup) {
-    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
+TEST_F(ResolverAtomicValidationTest, AddressSpace_WorkGroup) {
+    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverAtomicValidationTest, StorageClass_Storage) {
-    GlobalVar("g", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kStorage,
+TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage) {
+    GlobalVar("g", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kStorage,
               ast::Access::kReadWrite, Group(0_a), Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAtomicValidationTest, StorageClass_Storage_Struct) {
+TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage_Struct) {
     auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Group(0_a),
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
               Binding(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidType) {
-    GlobalVar("a", ty.atomic(ty.f32(Source{{12, 34}})), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic(ty.f32(Source{{12, 34}})), ast::AddressSpace::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Simple) {
-    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Simple) {
+    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: atomic variables must have <storage> or <workgroup> "
-              "storage class");
+              "address space");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Array) {
-    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Array) {
+    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: atomic variables must have <storage> or <workgroup> "
-              "storage class");
+              "address space");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Struct) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Struct) {
     auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class\n"
+              "address space\n"
               "note: atomic sub-type of 's' is declared here");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_StructOfStruct) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_StructOfStruct) {
     // struct Inner { m : atomic<i32>; };
     // struct Outer { m : array<Inner, 4>; };
     // var<private> g : Outer;
@@ -91,16 +91,16 @@
     auto* Inner =
         Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
     auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
-    GlobalVar("g", ty.Of(Outer), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(Outer), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class\n"
+              "address space\n"
               "note: atomic sub-type of 'Outer' is declared here");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_StructOfStructOfArray) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_StructOfStructOfArray) {
     // struct Inner { m : array<atomic<i32>, 4>; };
     // struct Outer { m : array<Inner, 4>; };
     // var<private> g : Outer;
@@ -108,46 +108,46 @@
     auto* Inner =
         Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
     auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
-    GlobalVar("g", ty.Of(Outer), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(Outer), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class\n"
+              "address space\n"
               "12:34 note: atomic sub-type of 'Outer' is declared here");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfArray) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfArray) {
     // type AtomicArray = array<atomic<i32>, 5>;
     // var<private> v: array<s, 5>;
 
     auto* atomic_array =
         Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
-    GlobalVar(Source{{56, 78}}, "v", ty.Of(atomic_array), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "v", ty.Of(atomic_array), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class");
+              "address space");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfStruct) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStruct) {
     // struct S{
     //   m: atomic<u32>;
     // };
     // var<private> v: array<S, 5u>;
 
     auto* s = Structure("S", utils::Vector{Member("m", ty.atomic<u32>())});
-    GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class\n"
+              "address space\n"
               "note: atomic sub-type of 'array<S, 5>' is declared here");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfStructOfArray) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStructOfArray) {
     // type AtomicArray = array<atomic<i32>, 5u>;
     // struct S{
     //   m: AtomicArray;
@@ -157,16 +157,16 @@
     auto* atomic_array =
         Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
     auto* s = Structure("S", utils::Vector{Member("m", ty.Of(atomic_array))});
-    GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class\n"
+              "address space\n"
               "note: atomic sub-type of 'array<S, 5>' is declared here");
 }
 
-TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Complex) {
+TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
     // type AtomicArray = array<atomic<i32>, 5u>;
     // struct S6 { x: array<i32, 4>; };
     // struct S5 { x: S6;
@@ -198,35 +198,35 @@
     auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
     auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
     auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "error: atomic variables must have <storage> or <workgroup> "
-              "storage class\n"
+              "address space\n"
               "note: atomic sub-type of 'S0' is declared here");
 }
 
 TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
     auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "error: atomic variables in <storage> storage class must have read_write "
+              "error: atomic variables in <storage> address space must have read_write "
               "access mode\n"
               "note: atomic sub-type of 's' is declared here");
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
     auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "error: atomic variables in <storage> storage class must have read_write "
+              "error: atomic variables in <storage> address space must have read_write "
               "access mode\n"
               "note: atomic sub-type of 's' is declared here");
 }
@@ -239,12 +239,12 @@
     auto* Inner =
         Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
     auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "error: atomic variables in <storage> storage class must have read_write "
+              "error: atomic variables in <storage> address space must have read_write "
               "access mode\n"
               "note: atomic sub-type of 'Outer' is declared here");
 }
@@ -257,12 +257,12 @@
     auto* Inner =
         Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
     auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "error: atomic variables in <storage> storage class must have "
+              "error: atomic variables in <storage> address space must have "
               "read_write access mode\n"
               "12:34 note: atomic sub-type of 'Outer' is declared here");
 }
@@ -299,12 +299,12 @@
     auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
     auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
     auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "error: atomic variables in <storage> storage class must have "
+              "error: atomic variables in <storage> address space must have "
               "read_write access mode\n"
               "note: atomic sub-type of 'S0' is declared here");
 }
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index e648562..796487b 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -89,7 +89,7 @@
                                                                 AttributeKind kind) {
     switch (kind) {
         case AttributeKind::kAlign:
-            return {builder.MemberAlign(source, 4_u)};
+            return {builder.MemberAlign(source, 4_i)};
         case AttributeKind::kBinding:
             return {builder.Binding(source, 1_a)};
         case AttributeKind::kBuiltin:
@@ -627,8 +627,8 @@
     Structure("mystruct", utils::Vector{
                               Member("a", ty.i32(),
                                      utils::Vector{
-                                         MemberAlign(Source{{12, 34}}, 4_u),
-                                         MemberAlign(Source{{56, 78}}, 8_u),
+                                         MemberAlign(Source{{12, 34}}, 4_i),
+                                         MemberAlign(Source{{56, 78}}, 8_i),
                                      }),
                           });
     EXPECT_FALSE(r()->Resolve());
@@ -659,6 +659,88 @@
               "position builtin");
 }
 
+TEST_F(StructMemberAttributeTest, Align_Attribute_Const) {
+    GlobalConst("val", ty.i32(), Expr(1_i));
+
+    Structure("mystruct", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberAlign("val")})});
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_ConstNegative) {
+    GlobalConst("val", ty.i32(), Expr(-2_i));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(12:34 error: 'align' value must be a positive, power-of-two integer)");
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_ConstPowerOfTwo) {
+    GlobalConst("val", ty.i32(), Expr(3_i));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(12:34 error: 'align' value must be a positive, power-of-two integer)");
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_ConstF32) {
+    GlobalConst("val", ty.f32(), Expr(1.23_f));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'align' must be an i32 or u32 value)");
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_ConstU32) {
+    GlobalConst("val", ty.u32(), Expr(2_u));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_TRUE(r()->Resolve());
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_ConstAInt) {
+    GlobalConst("val", Expr(2_a));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_ConstAFloat) {
+    GlobalConst("val", Expr(2.0_a));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'align' must be an i32 or u32 value)");
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_Var) {
+    GlobalVar(Source{{1, 2}}, "val", ty.f32(), ast::AddressSpace::kPrivate, ast::Access::kUndefined,
+              Expr(1.23_f));
+
+    Structure(Source{{6, 4}}, "mystruct",
+              utils::Vector{Member(Source{{12, 5}}, "a", ty.f32(),
+                                   utils::Vector{MemberAlign(Expr(Source{{12, 35}}, "val"))})});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:35 error: var 'val' cannot be referenced at module-scope
+1:2 note: var 'val' declared here)");
+}
+
+TEST_F(StructMemberAttributeTest, Align_Attribute_Override) {
+    Override("val", ty.f32(), Expr(1.23_f));
+
+    Structure("mystruct", utils::Vector{Member(
+                              "a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'align' must be an i32 or u32 value)");
+}
+
 }  // namespace StructAndStructMemberTests
 
 using ArrayAttributeTest = TestWithParams;
@@ -703,7 +785,7 @@
     if (IsBindingAttribute(params.kind)) {
         GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), attrs);
     } else {
-        GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, attrs);
+        GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate, attrs);
     }
 
     if (params.should_pass) {
@@ -874,7 +956,7 @@
                              create<ast::StrideAttribute>(Source{{12, 34}}, params.stride),
                          });
 
-    GlobalVar("myarray", arr, ast::StorageClass::kPrivate);
+    GlobalVar("myarray", arr, ast::AddressSpace::kPrivate);
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -957,7 +1039,7 @@
                              create<ast::StrideAttribute>(Source{{56, 78}}, 4u),
                          });
 
-    GlobalVar("myarray", arr, ast::StorageClass::kPrivate);
+    GlobalVar("myarray", arr, ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -976,7 +1058,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("x", ty.i32()),
                              });
-    GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
+    GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::AddressSpace::kUniform);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -987,7 +1069,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("x", ty.i32()),
                              });
-    GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead);
+    GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1073,7 +1155,7 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPointOnNonResource) {
-    GlobalVar(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate, Binding(1_a),
+    GlobalVar(Source{{12, 34}}, "G", ty.f32(), ast::AddressSpace::kPrivate, Binding(1_a),
               Group(2_a));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index b1d1e35..f3ae845 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -78,7 +78,7 @@
 TEST_P(ResolverBuiltinTest_BoolMethod, Scalar) {
     auto name = GetParam();
 
-    GlobalVar("my_var", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call(name, "my_var");
     WrapInFunction(expr);
@@ -91,7 +91,7 @@
 TEST_P(ResolverBuiltinTest_BoolMethod, Vector) {
     auto name = GetParam();
 
-    GlobalVar("my_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<bool>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call(name, "my_var");
     WrapInFunction(expr);
@@ -106,9 +106,9 @@
                          testing::Values("any", "all"));
 
 TEST_F(ResolverBuiltinTest, Select) {
-    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
-    GlobalVar("bool_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("bool_var", ty.vec3<bool>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("select", "my_var", "my_var", "bool_var");
     WrapInFunction(expr);
@@ -131,9 +131,9 @@
               R"(error: no matching call to select()
 
 3 candidate functions:
-  select(T, T, bool) -> T  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
+  select(T, T, bool) -> T  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
 )");
 }
 
@@ -147,9 +147,9 @@
               R"(error: no matching call to select(i32, i32, i32)
 
 3 candidate functions:
-  select(T, T, bool) -> T  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
+  select(T, T, bool) -> T  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
 )");
 }
 
@@ -164,9 +164,9 @@
               R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
 
 3 candidate functions:
-  select(T, T, bool) -> T  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
+  select(T, T, bool) -> T  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
 )");
 }
 
@@ -180,9 +180,9 @@
               R"(error: no matching call to select(f32, vec2<f32>, bool)
 
 3 candidate functions:
-  select(T, T, bool) -> T  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
+  select(T, T, bool) -> T  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
 )");
 }
 
@@ -196,9 +196,9 @@
               R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
 
 3 candidate functions:
-  select(T, T, bool) -> T  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
-  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is f32, f16, i32, u32 or bool
+  select(T, T, bool) -> T  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, bool) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
 )");
 }
 
@@ -212,7 +212,7 @@
 TEST_F(ResolverBuiltinArrayTest, ArrayLength_Vector) {
     auto* ary = ty.array<i32>();
     auto* str = Structure("S", utils::Vector{Member("x", ary)});
-    GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("a", ty.Of(str), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     auto* call = Call("arrayLength", AddressOf(MemberAccessor("a", "x")));
@@ -225,7 +225,7 @@
 }
 
 TEST_F(ResolverBuiltinArrayTest, ArrayLength_Error_ArraySized) {
-    GlobalVar("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
+    GlobalVar("arr", ty.array<i32, 4>(), ast::AddressSpace::kPrivate);
     auto* call = Call("arrayLength", AddressOf("arr"));
     WrapInFunction(call);
 
@@ -1029,7 +1029,7 @@
 }
 
 TEST_F(ResolverBuiltinFloatTest, Frexp_Error_FirstParamInt) {
-    GlobalVar("v", ty.i32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("v", ty.i32(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("frexp", 1_i, AddressOf("v"));
     WrapInFunction(call);
 
@@ -1045,7 +1045,7 @@
 }
 
 TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamFloatPtr) {
-    GlobalVar("v", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("v", ty.f32(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("frexp", 1_f, AddressOf("v"));
     WrapInFunction(call);
 
@@ -1075,7 +1075,7 @@
 }
 
 TEST_F(ResolverBuiltinFloatTest, Frexp_Error_VectorSizesDontMatch) {
-    GlobalVar("v", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("v", ty.vec4<i32>(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("frexp", vec2<f32>(1_f, 2_f), AddressOf("v"));
     WrapInFunction(call);
 
@@ -1324,7 +1324,7 @@
 }
 
 TEST_F(ResolverBuiltinFloatTest, Modf_Error_FirstParamInt) {
-    GlobalVar("whole", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("whole", ty.f32(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("modf", 1_i, AddressOf("whole"));
     WrapInFunction(call);
 
@@ -1340,7 +1340,7 @@
 }
 
 TEST_F(ResolverBuiltinFloatTest, Modf_Error_SecondParamIntPtr) {
-    GlobalVar("whole", ty.i32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("whole", ty.i32(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("modf", 1_f, AddressOf("whole"));
     WrapInFunction(call);
 
@@ -1370,7 +1370,7 @@
 }
 
 TEST_F(ResolverBuiltinFloatTest, Modf_Error_VectorSizesDontMatch) {
-    GlobalVar("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("whole", ty.vec4<f32>(), ast::AddressSpace::kWorkgroup);
     auto* call = Call("modf", vec2<f32>(1_f, 2_f), AddressOf("whole"));
     WrapInFunction(call);
 
@@ -1850,7 +1850,7 @@
 namespace matrix_builtin_tests {
 
 TEST_F(ResolverBuiltinTest, Determinant_2x2_f32) {
-    GlobalVar("var", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat2x2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1864,7 +1864,7 @@
 TEST_F(ResolverBuiltinTest, Determinant_2x2_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("var", ty.mat2x2<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat2x2<f16>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1876,7 +1876,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_3x3_f32) {
-    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1890,7 +1890,7 @@
 TEST_F(ResolverBuiltinTest, Determinant_3x3_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("var", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1902,7 +1902,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_4x4_f32) {
-    GlobalVar("var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat4x4<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1916,7 +1916,7 @@
 TEST_F(ResolverBuiltinTest, Determinant_4x4_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("var", ty.mat4x4<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat4x4<f16>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1928,7 +1928,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_NotSquare) {
-    GlobalVar("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat2x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1943,7 +1943,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_NotMatrix) {
-    GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1963,7 +1963,7 @@
 namespace vector_builtin_tests {
 
 TEST_F(ResolverBuiltinTest, Dot_Vec2_f32) {
-    GlobalVar("my_var", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -1977,7 +1977,7 @@
 TEST_F(ResolverBuiltinTest, Dot_Vec2_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("my_var", ty.vec2<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec2<f16>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -1989,7 +1989,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Dot_Vec3_i32) {
-    GlobalVar("my_var", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -2001,7 +2001,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Dot_Vec4_u32) {
-    GlobalVar("my_var", ty.vec4<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec4<u32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -2036,7 +2036,7 @@
 TEST_P(ResolverBuiltinDerivativeTest, Scalar) {
     auto name = GetParam();
 
-    GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call(name, "ident");
     Func("func", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
@@ -2050,7 +2050,7 @@
 
 TEST_P(ResolverBuiltinDerivativeTest, Vector) {
     auto name = GetParam();
-    GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call(name, "ident");
     Func("func", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
@@ -2146,7 +2146,7 @@
             GlobalVar(name, type, Binding(0_a), Group(0_a));
 
         } else {
-            GlobalVar(name, type, ast::StorageClass::kPrivate);
+            GlobalVar(name, type, ast::AddressSpace::kPrivate);
         }
 
         call_params->Push(Expr(name));
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index 63f3ccb..6ba48de 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -113,7 +113,7 @@
 }
 
 TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVar) {
-    GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index a27052c..f2bf191 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -100,7 +100,7 @@
 TEST_P(ResolverBuiltinsStageTest, All_input) {
     const Params& params = GetParam();
 
-    auto* p = GlobalVar("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    auto* p = GlobalVar("p", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* input = Param("input", params.type(*this),
                         utils::Vector{Builtin(Source{{12, 34}}, params.builtin)});
     switch (params.stage) {
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 3fa71b8..6c64fad 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -103,7 +103,7 @@
     //   var z: i32 = 1i;
     //   foo(&z);
     // }
-    auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
     Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
@@ -120,7 +120,7 @@
     //   let z: i32 = 1i;
     //   foo(&z);
     // }
-    auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
     Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
@@ -142,7 +142,7 @@
     auto* S = Structure("S", utils::Vector{
                                  Member("m", ty.i32()),
                              });
-    auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
     Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
@@ -166,7 +166,7 @@
     auto* S = Structure("S", utils::Vector{
                                  Member("m", ty.i32()),
                              });
-    auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+    auto* param = Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction));
     Func("foo", utils::Vector{param}, ty.void_(), utils::Empty);
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
@@ -185,12 +185,12 @@
     // }
     Func("foo",
          utils::Vector{
-             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
          ty.void_(), utils::Empty);
     Func("bar",
          utils::Vector{
-             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
          ty.void_(),
          utils::Vector{
@@ -212,12 +212,12 @@
     // }
     Func("foo",
          utils::Vector{
-             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
          ty.void_(), utils::Empty);
     Func("bar",
          utils::Vector{
-             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
          ty.void_(),
          utils::Vector{
@@ -245,11 +245,11 @@
     // }
     Func("x",
          utils::Vector{
-             Param("p", ty.pointer<i32>(ast::StorageClass::kFunction)),
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kFunction)),
          },
          ty.void_(), utils::Empty);
     auto* v = Var("v", ty.i32());
-    auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(v));
+    auto* p = Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kFunction), AddressOf(v));
     auto* c = Var("c", ty.i32(), Call("x", Expr(Source{{12, 34}}, p)));
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
@@ -276,11 +276,11 @@
     // }
     Func("foo",
          utils::Vector{
-             Param("p", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+             Param("p", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
          },
          ty.void_(), utils::Empty);
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(v));
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* p = Let("p", ty.pointer(ty.i32(), ast::AddressSpace::kPrivate), AddressOf(v));
     auto* c = Var("c", ty.i32(), Call("foo", Expr(Source{{12, 34}}, p)));
     Func("main", utils::Empty, ty.void_(),
          utils::Vector{
@@ -301,7 +301,7 @@
     // fn f() {
     //   v();
     // }
-    GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     Func("f", utils::Empty, ty.void_(),
          utils::Vector{
              CallStmt(Call(Source{{12, 34}}, "v")),
diff --git a/src/tint/resolver/compound_assignment_validation_test.cc b/src/tint/resolver/compound_assignment_validation_test.cc
index 9112fa0..dfb6926 100644
--- a/src/tint/resolver/compound_assignment_validation_test.cc
+++ b/src/tint/resolver/compound_assignment_validation_test.cc
@@ -51,7 +51,7 @@
     // var a : i32;
     // let b : ptr<function,i32> = &a;
     // *b += 2;
-    const auto func = ast::StorageClass::kFunction;
+    const auto func = ast::AddressSpace::kFunction;
     auto* var_a = Var("a", ty.i32(), func, Expr(2_i));
     auto* var_b = Let("b", ty.pointer<i32>(func), AddressOf(Expr("a")));
     WrapInFunction(var_a, var_b,
@@ -233,7 +233,7 @@
     // {
     //   a += 1i;
     // }
-    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
     WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", 1_i, ast::BinaryOp::kAdd));
 
@@ -264,7 +264,7 @@
 TEST_F(ResolverCompoundAssignmentValidationTest, LhsAtomic) {
     // var<workgroup> a : atomic<i32>;
     // a += a;
-    GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::AddressSpace::kWorkgroup);
     WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", "a", ast::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index 62e011c..abc7ad4 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -503,25 +503,30 @@
     }
 }
 
-/// TransformElements constructs a new constant of type `composite_ty` by applying the
-/// transformation function 'f' on each of the most deeply nested elements of 'cs'. Assumes that all
-/// input constants `cs` are of the same type.
+namespace detail {
+/// Implementation of TransformElements
 template <typename F, typename... CONSTANTS>
 ImplResult TransformElements(ProgramBuilder& builder,
                              const sem::Type* composite_ty,
                              F&& f,
+                             size_t index,
                              CONSTANTS&&... cs) {
     uint32_t n = 0;
     auto* ty = First(cs...)->Type();
     auto* el_ty = sem::Type::ElementOf(ty, &n);
     if (el_ty == ty) {
-        return f(cs...);
+        constexpr bool kHasIndexParam = traits::IsType<size_t, traits::LastParameterType<F>>;
+        if constexpr (kHasIndexParam) {
+            return f(cs..., index);
+        } else {
+            return f(cs...);
+        }
     }
     utils::Vector<const sem::Constant*, 8> els;
     els.Reserve(n);
     for (uint32_t i = 0; i < n; i++) {
-        if (auto el = TransformElements(builder, sem::Type::ElementOf(composite_ty),
-                                        std::forward<F>(f), cs->Index(i)...)) {
+        if (auto el = detail::TransformElements(builder, sem::Type::ElementOf(composite_ty),
+                                                std::forward<F>(f), index + i, cs->Index(i)...)) {
             els.Push(el.Get());
 
         } else {
@@ -530,10 +535,24 @@
     }
     return CreateComposite(builder, composite_ty, std::move(els));
 }
+}  // namespace detail
+
+/// TransformElements constructs a new constant of type `composite_ty` by applying the
+/// transformation function `f` on each of the most deeply nested elements of 'cs'. Assumes that all
+/// input constants `cs` are of the same arity (all scalars or all vectors of the same size).
+/// If `f`'s last argument is a `size_t`, then the index of the most deeply nested element inside
+/// the most deeply nested aggregate type will be passed in.
+template <typename F, typename... CONSTANTS>
+ImplResult TransformElements(ProgramBuilder& builder,
+                             const sem::Type* composite_ty,
+                             F&& f,
+                             CONSTANTS&&... cs) {
+    return detail::TransformElements(builder, composite_ty, f, 0, cs...);
+}
 
 /// TransformBinaryElements constructs a new constant of type `composite_ty` by applying the
 /// transformation function 'f' on each of the most deeply nested elements of both `c0` and `c1`.
-/// Unlike TransformElements, this function handles the constants being of different types, e.g.
+/// Unlike TransformElements, this function handles the constants being of different arity, e.g.
 /// vector-scalar, scalar-vector.
 template <typename F>
 ImplResult TransformBinaryElements(ProgramBuilder& builder,
@@ -1516,6 +1535,35 @@
     return TransformElements(builder, ty, transform, args[0], args[1], args[2]);
 }
 
+ConstEval::Result ConstEval::select_bool(const sem::Type* ty,
+                                         utils::VectorRef<const sem::Constant*> args,
+                                         const Source&) {
+    auto cond = args[2]->As<bool>();
+    auto transform = [&](const sem::Constant* c0, const sem::Constant* c1) {
+        auto create = [&](auto f, auto t) -> ImplResult {
+            return CreateElement(builder, sem::Type::DeepestElementOf(ty), cond ? t : f);
+        };
+        return Dispatch_fia_fiu32_f16_bool(create, c0, c1);
+    };
+
+    return TransformElements(builder, ty, transform, args[0], args[1]);
+}
+
+ConstEval::Result ConstEval::select_boolvec(const sem::Type* ty,
+                                            utils::VectorRef<const sem::Constant*> args,
+                                            const Source&) {
+    auto transform = [&](const sem::Constant* c0, const sem::Constant* c1, size_t index) {
+        auto create = [&](auto f, auto t) -> ImplResult {
+            // Get corresponding bool value at the current vector value index
+            auto cond = args[2]->Index(index)->As<bool>();
+            return CreateElement(builder, sem::Type::DeepestElementOf(ty), cond ? t : f);
+        };
+        return Dispatch_fia_fiu32_f16_bool(create, c0, c1);
+    };
+
+    return TransformElements(builder, ty, transform, args[0], args[1]);
+}
+
 ConstEval::Result ConstEval::Convert(const sem::Type* target_ty,
                                      const sem::Constant* value,
                                      const Source& source) {
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index 81ec3f7..b58ed41 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -395,6 +395,24 @@
                  utils::VectorRef<const sem::Constant*> args,
                  const Source& source);
 
+    /// select builtin with single bool third arg
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result select_bool(const sem::Type* ty,
+                       utils::VectorRef<const sem::Constant*> args,
+                       const Source& source);
+
+    /// select builtin with vector of bool third arg
+    /// @param ty the expression type
+    /// @param args the input arguments
+    /// @param source the source location of the conversion
+    /// @return the result value, or null if the value cannot be calculated
+    Result select_boolvec(const sem::Type* ty,
+                          utils::VectorRef<const sem::Constant*> args,
+                          const Source& source);
+
   private:
     /// Adds the given error message to the diagnostics
     void AddError(const std::string& msg, const Source& source) const;
diff --git a/src/tint/resolver/const_eval_test.cc b/src/tint/resolver/const_eval_test.cc
index 5f5b486..8bded4c 100644
--- a/src/tint/resolver/const_eval_test.cc
+++ b/src/tint/resolver/const_eval_test.cc
@@ -2666,7 +2666,7 @@
 
 TEST_F(ResolverConstEvalTest, RuntimeArray_vec3_f32_Index_OOB_Low) {
     auto* sb = GlobalVar("sb", ty.array(ty.vec3<f32>()), Group(0_a), Binding(0_a),
-                         ast::StorageClass::kStorage);
+                         ast::AddressSpace::kStorage);
     auto* expr = IndexAccessor(sb, Expr(Source{{12, 34}}, -2_i));
     WrapInFunction(expr);
 
@@ -2879,6 +2879,7 @@
     Value<builder::vec3<i32>>,
     Value<builder::vec3<f32>>,
     Value<builder::vec3<f16>>,
+    Value<builder::vec3<bool>>,
 
     Value<builder::vec4<AInt>>,
     Value<builder::vec4<AFloat>>,
@@ -2886,6 +2887,7 @@
     Value<builder::vec4<i32>>,
     Value<builder::vec4<f32>>,
     Value<builder::vec4<f16>>,
+    Value<builder::vec4<bool>>,
 
     Value<builder::mat2x2<AInt>>,
     Value<builder::mat2x2<AFloat>>,
@@ -2981,6 +2983,7 @@
 // Unary op
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 namespace unary_op {
+// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
 using resolver::operator<<;
 
 struct Case {
@@ -3035,7 +3038,7 @@
             ForEachElemPair(value, expected_value,
                             [&](const sem::Constant* a, const sem::Constant* b) {
                                 EXPECT_EQ(a->As<T>(), b->As<T>());
-                                if constexpr (IsIntegral<UnwrapNumber<T>>) {
+                                if constexpr (IsIntegral<T>) {
                                     // Check that the constant's integer doesn't contain unexpected
                                     // data in the MSBs that are outside of the bit-width of T.
                                     EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
@@ -3151,6 +3154,7 @@
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 namespace binary_op {
+// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
 using resolver::operator<<;
 
 struct Case {
@@ -3166,7 +3170,7 @@
     return Case{std::move(lhs), std::move(rhs), std::move(expected), overflow};
 }
 
-/// Convenience overload to creates a Case with just scalars
+/// Convenience overload that creates a Case with just scalars
 template <typename T, typename U, typename V, typename = std::enable_if_t<!IsValue<T>>>
 Case C(T lhs, U rhs, V expected, bool overflow = false) {
     return Case{Val(lhs), Val(rhs), Val(expected), overflow};
@@ -3216,7 +3220,7 @@
             ForEachElemPair(value, expected_value,
                             [&](const sem::Constant* a, const sem::Constant* b) {
                                 EXPECT_EQ(a->As<T>(), b->As<T>());
-                                if constexpr (IsIntegral<UnwrapNumber<T>>) {
+                                if constexpr (IsIntegral<T>) {
                                     // Check that the constant's integer doesn't contain unexpected
                                     // data in the MSBs that are outside of the bit-width of T.
                                     EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
@@ -3238,7 +3242,7 @@
 
 template <typename T>
 std::vector<Case> OpAddIntCases() {
-    static_assert(IsIntegral<UnwrapNumber<T>>);
+    static_assert(IsIntegral<T>);
     return {
         C(T{0}, T{0}, T{0}),
         C(T{1}, T{2}, T{3}),
@@ -3251,7 +3255,7 @@
 }
 template <typename T>
 std::vector<Case> OpAddFloatCases() {
-    static_assert(IsFloatingPoint<UnwrapNumber<T>>);
+    static_assert(IsFloatingPoint<T>);
     return {
         C(T{0}, T{0}, T{0}),
         C(T{1}, T{2}, T{3}),
@@ -3275,7 +3279,7 @@
 
 template <typename T>
 std::vector<Case> OpSubIntCases() {
-    static_assert(IsIntegral<UnwrapNumber<T>>);
+    static_assert(IsIntegral<T>);
     return {
         C(T{0}, T{0}, T{0}),
         C(T{3}, T{2}, T{1}),
@@ -3288,7 +3292,7 @@
 }
 template <typename T>
 std::vector<Case> OpSubFloatCases() {
-    static_assert(IsFloatingPoint<UnwrapNumber<T>>);
+    static_assert(IsFloatingPoint<T>);
     return {
         C(T{0}, T{0}, T{0}),
         C(T{3}, T{2}, T{1}),
@@ -4051,25 +4055,55 @@
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
 namespace builtin {
-
-using Types = std::variant<AInt, AFloat, u32, i32, f32, f16>;
+// Bring in std::ostream& operator<<(std::ostream& o, const Types& types)
+using resolver::operator<<;
 
 struct Case {
+    Case(utils::VectorRef<Types> in_args, Types in_expected)
+        : args(std::move(in_args)), expected(std::move(in_expected)) {}
+
+    /// Expected value may be positive or negative
+    Case& PosOrNeg() {
+        expected_pos_or_neg = true;
+        return *this;
+    }
+
+    /// Expected value should be compared using FLOAT_EQ instead of EQ
+    Case& FloatComp() {
+        float_compare = true;
+        return *this;
+    }
+
     utils::Vector<Types, 8> args;
-    Types result;
-    bool result_pos_or_neg;
+    Types expected;
+    bool expected_pos_or_neg = false;
+    bool float_compare = false;
 };
 
 static std::ostream& operator<<(std::ostream& o, const Case& c) {
+    o << "args: ";
     for (auto& a : c.args) {
-        std::visit([&](auto&& v) { o << v << ((&a != &c.args.Back()) ? " " : ""); }, a);
+        o << a << ", ";
     }
+    o << "expected: " << c.expected << ", expected_pos_or_neg: " << c.expected_pos_or_neg;
     return o;
 }
 
-template <typename T>
-Case C(std::initializer_list<Types> args, T result, bool result_pos_or_neg = false) {
-    return Case{std::move(args), std::move(result), result_pos_or_neg};
+/// Creates a Case with Values for args and result
+static Case C(std::initializer_list<Types> args, Types result) {
+    return Case{utils::Vector<Types, 8>{args}, std::move(result)};
+}
+
+/// Convenience overload that creates a Case with just scalars
+using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
+static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
+    utils::Vector<Types, 8> args;
+    for (auto& sa : sargs) {
+        std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
+    }
+    Types result = Val(0_a);
+    std::visit([&](auto&& v) { result = Val(v); }, sresult);
+    return Case{std::move(args), std::move(result)};
 }
 
 using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
@@ -4078,19 +4112,21 @@
     Enable(ast::Extension::kF16);
 
     auto builtin = std::get<0>(GetParam());
-    auto c = std::get<1>(GetParam());
+    auto& c = std::get<1>(GetParam());
 
     utils::Vector<const ast::Expression*, 8> args;
     for (auto& a : c.args) {
-        std::visit([&](auto&& v) { args.Push(Expr(v)); }, a);
+        std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
     }
 
     std::visit(
-        [&](auto&& result) {
-            using T = std::decay_t<decltype(result)>;
+        [&](auto&& expected) {
+            using T = typename std::decay_t<decltype(expected)>::ElementType;
             auto* expr = Call(sem::str(builtin), std::move(args));
 
             GlobalConst("C", expr);
+            auto* expected_expr = expected.Expr(*this);
+            GlobalConst("E", expected_expr);
 
             EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -4099,64 +4135,92 @@
             ASSERT_NE(value, nullptr);
             EXPECT_TYPE(value->Type(), sem->Type());
 
-            auto actual = value->As<T>();
+            auto* expected_sem = Sem().Get(expected_expr);
+            const sem::Constant* expected_value = expected_sem->ConstantValue();
+            ASSERT_NE(expected_value, nullptr);
+            EXPECT_TYPE(expected_value->Type(), expected_sem->Type());
 
-            if constexpr (IsFloatingPoint<UnwrapNumber<T>>) {
-                if (std::isnan(result)) {
-                    EXPECT_TRUE(std::isnan(actual));
-                } else {
-                    EXPECT_FLOAT_EQ(c.result_pos_or_neg ? Abs(actual) : actual, result);
-                }
-            } else {
-                EXPECT_EQ(c.result_pos_or_neg ? Abs(actual) : actual, result);
-            }
-
-            if constexpr (IsIntegral<UnwrapNumber<T>>) {
-                // Check that the constant's integer doesn't contain unexpected data in the MSBs
-                // that are outside of the bit-width of T.
-                EXPECT_EQ(value->As<AInt>(), AInt(result));
-            }
+            ForEachElemPair(value, expected_value,
+                            [&](const sem::Constant* a, const sem::Constant* b) {
+                                auto v = a->As<T>();
+                                auto e = b->As<T>();
+                                if constexpr (std::is_same_v<bool, T>) {
+                                    EXPECT_EQ(v, e);
+                                } else if constexpr (IsFloatingPoint<T>) {
+                                    if (std::isnan(e)) {
+                                        EXPECT_TRUE(std::isnan(v));
+                                    } else {
+                                        auto vf = (c.expected_pos_or_neg ? Abs(v) : v);
+                                        if (c.float_compare) {
+                                            EXPECT_FLOAT_EQ(vf, e);
+                                        } else {
+                                            EXPECT_EQ(vf, e);
+                                        }
+                                    }
+                                } else {
+                                    EXPECT_EQ((c.expected_pos_or_neg ? Abs(v) : v), e);
+                                    // Check that the constant's integer doesn't contain unexpected
+                                    // data in the MSBs that are outside of the bit-width of T.
+                                    EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
+                                }
+                                return HasFailure() ? Action::kStop : Action::kContinue;
+                            });
         },
-        c.result);
+        c.expected);
 }
 
+INSTANTIATE_TEST_SUITE_P(  //
+    MixedAbstractArgs,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
+                     testing::ValuesIn(std::vector{
+                         C({0_a, -0.0_a}, kPi<AFloat>),
+                         C({1.0_a, 0_a}, kPiOver2<AFloat>),
+                     })));
+
 template <typename T, bool finite_only>
 std::vector<Case> Atan2Cases() {
     std::vector<Case> cases = {
         // If y is +/-0 and x is negative or -0, +/-PI is returned
-        C({T(0.0), -T(0.0)}, kPi<T>, true),
+        C({T(0.0), -T(0.0)}, kPi<T>).PosOrNeg().FloatComp(),
 
         // If y is +/-0 and x is positive or +0, +/-0 is returned
-        C({T(0.0), T(0.0)}, T(0.0), true),
+        C({T(0.0), T(0.0)}, T(0.0)).PosOrNeg(),
 
         // If x is +/-0 and y is negative, -PI/2 is returned
-        C({-T(1.0), T(0.0)}, -kPiOver2<T>),
-        C({-T(1.0), -T(0.0)}, -kPiOver2<T>),
+        C({-T(1.0), T(0.0)}, -kPiOver2<T>).FloatComp(),  //
+        C({-T(1.0), -T(0.0)}, -kPiOver2<T>).FloatComp(),
 
         // If x is +/-0 and y is positive, +PI/2 is returned
-        C({T(1.0), T(0.0)}, kPiOver2<T>),
-        C({T(1.0), -T(0.0)}, kPiOver2<T>),
+        C({T(1.0), T(0.0)}, kPiOver2<T>).FloatComp(),  //
+        C({T(1.0), -T(0.0)}, kPiOver2<T>).FloatComp(),
+
+        // Vector tests
+        C({Vec(T(0.0), T(0.0)), Vec(-T(0.0), T(0.0))}, Vec(kPi<T>, T(0.0))).PosOrNeg().FloatComp(),
+        C({Vec(-T(1.0), -T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(-kPiOver2<T>, -kPiOver2<T>))
+            .FloatComp(),
+        C({Vec(T(1.0), T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(kPiOver2<T>, kPiOver2<T>)).FloatComp(),
     };
 
     if constexpr (!finite_only) {
         std::vector<Case> non_finite_cases = {
             // If y is +/-INF and x is finite, +/-PI/2 is returned
-            C({T::Inf(), T(0.0)}, kPiOver2<T>, true),
-            C({-T::Inf(), T(0.0)}, kPiOver2<T>, true),
+            C({T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
+            C({-T::Inf(), T(0.0)}, kPiOver2<T>).PosOrNeg().FloatComp(),
 
             // If y is +/-INF and x is -INF, +/-3PI/4 is returned
-            C({T::Inf(), -T::Inf()}, k3PiOver4<T>, true),
-            C({-T::Inf(), -T::Inf()}, k3PiOver4<T>, true),
+            C({T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
+            C({-T::Inf(), -T::Inf()}, k3PiOver4<T>).PosOrNeg().FloatComp(),
 
             // If y is +/-INF and x is +INF, +/-PI/4 is returned
-            C({T::Inf(), T::Inf()}, kPiOver4<T>, true),
-            C({-T::Inf(), T::Inf()}, kPiOver4<T>, true),
+            C({T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
+            C({-T::Inf(), T::Inf()}, kPiOver4<T>).PosOrNeg().FloatComp(),
 
             // If x is -INF and y is finite and positive, +PI is returned
-            C({T(0.0), -T::Inf()}, kPi<T>),
+            C({T(0.0), -T::Inf()}, kPi<T>).FloatComp(),
 
             // If x is -INF and y is finite and negative, -PI is returned
-            C({-T(0.0), -T::Inf()}, -kPi<T>),
+            C({-T(0.0), -T::Inf()}, -kPi<T>).FloatComp(),
 
             // If x is +INF and y is finite and positive, +0 is returned
             C({T(0.0), T::Inf()}, T(0.0)),
@@ -4168,23 +4232,19 @@
             C({T::NaN(), T(0.0)}, T::NaN()),
             C({T(0.0), T::NaN()}, T::NaN()),
             C({T::NaN(), T::NaN()}, T::NaN()),
-        };
 
+            // Vector tests
+            C({Vec(T::Inf(), -T::Inf(), T::Inf(), -T::Inf()),  //
+               Vec(T(0.0), T(0.0), -T::Inf(), -T::Inf())},     //
+              Vec(kPiOver2<T>, kPiOver2<T>, k3PiOver4<T>, k3PiOver4<T>))
+                .PosOrNeg()
+                .FloatComp(),
+        };
         cases = Concat(cases, non_finite_cases);
     }
 
     return cases;
 }
-
-INSTANTIATE_TEST_SUITE_P(  //
-    MixedAbstractArgs,
-    ResolverConstEvalBuiltinTest,
-    testing::Combine(testing::Values(sem::BuiltinType::kAtan2),
-                     testing::ValuesIn(std::vector{
-                         C({1_a, 1.0_a}, 0.78539819_a),
-                         C({1.0_a, 1_a}, 0.78539819_a),
-                     })));
-
 INSTANTIATE_TEST_SUITE_P(  //
     Atan2,
     ResolverConstEvalBuiltinTest,
@@ -4205,9 +4265,18 @@
         C({T::Lowest(), T::Lowest(), T::Lowest()}, T::Lowest()),
         C({T::Highest(), T::Lowest(), T::Highest()}, T::Highest()),
         C({T::Lowest(), T::Lowest(), T::Highest()}, T::Lowest()),
+
+        // Vector tests
+        C({Vec(T(0), T(0)),                         //
+           Vec(T(0), T(42)),                        //
+           Vec(T(0), T::Highest())},                //
+          Vec(T(0), T(42))),                        //
+        C({Vec(T::Lowest(), T(0), T(0)),            //
+           Vec(T(0), T::Lowest(), T::Highest()),    //
+           Vec(T(42), T::Highest(), T::Lowest())},  //
+          Vec(T(0), T(0), T::Lowest())),
     };
 }
-
 INSTANTIATE_TEST_SUITE_P(  //
     Clamp,
     ResolverConstEvalBuiltinTest,
@@ -4219,6 +4288,52 @@
                                               ClampCases<f32>(),
                                               ClampCases<f16>()))));
 
+template <typename T>
+std::vector<Case> SelectCases() {
+    return {
+        C({Val(T{1}), Val(T{2}), Val(false)}, Val(T{1})),
+        C({Val(T{1}), Val(T{2}), Val(true)}, Val(T{2})),
+
+        C({Val(T{2}), Val(T{1}), Val(false)}, Val(T{2})),
+        C({Val(T{2}), Val(T{1}), Val(true)}, Val(T{1})),
+
+        C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, false)}, Vec(T{1}, T{2})),
+        C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, true)}, Vec(T{1}, T{4})),
+        C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, false)}, Vec(T{3}, T{2})),
+        C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, true)}, Vec(T{3}, T{4})),
+
+        C({Vec(T{1}, T{1}, T{2}, T{2}),     //
+           Vec(T{2}, T{2}, T{1}, T{1}),     //
+           Vec(false, true, false, true)},  //
+          Vec(T{1}, T{2}, T{2}, T{1})),     //
+    };
+}
+static std::vector<Case> SelectBoolCases() {
+    return {
+        C({Val(true), Val(false), Val(false)}, Val(true)),
+        C({Val(true), Val(false), Val(true)}, Val(false)),
+
+        C({Val(false), Val(true), Val(true)}, Val(true)),
+        C({Val(false), Val(true), Val(false)}, Val(false)),
+
+        C({Vec(true, true, false, false),   //
+           Vec(false, false, true, true),   //
+           Vec(false, true, true, false)},  //
+          Vec(true, false, true, false)),   //
+    };
+}
+INSTANTIATE_TEST_SUITE_P(  //
+    Select,
+    ResolverConstEvalBuiltinTest,
+    testing::Combine(testing::Values(sem::BuiltinType::kSelect),
+                     testing::ValuesIn(Concat(SelectCases<AInt>(),  //
+                                              SelectCases<i32>(),
+                                              SelectCases<u32>(),
+                                              SelectCases<AFloat>(),
+                                              SelectCases<f32>(),
+                                              SelectCases<f16>(),
+                                              SelectBoolCases()))));
+
 }  // namespace builtin
 
 }  // namespace
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 823dcad..0b391c5 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -182,6 +182,7 @@
             [&](const ast::Struct* str) {
                 Declare(str->name, str);
                 for (auto* member : str->members) {
+                    TraverseAttributes(member->attributes);
                     TraverseType(member->type);
                 }
             },
@@ -421,11 +422,16 @@
             TraverseExpression(wg->z);
             return;
         }
+        if (auto* align = attr->As<ast::StructMemberAlignAttribute>()) {
+            TraverseExpression(align->expr);
+            return;
+        }
+
         if (attr->IsAnyOf<ast::BindingAttribute, ast::BuiltinAttribute, ast::GroupAttribute,
                           ast::IdAttribute, ast::InternalAttribute, ast::InterpolateAttribute,
                           ast::InvariantAttribute, ast::LocationAttribute, ast::StageAttribute,
-                          ast::StrideAttribute, ast::StructMemberAlignAttribute,
-                          ast::StructMemberOffsetAttribute, ast::StructMemberSizeAttribute>()) {
+                          ast::StrideAttribute, ast::StructMemberOffsetAttribute,
+                          ast::StructMemberSizeAttribute>()) {
             return;
         }
 
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 224d342..614873e 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -425,7 +425,7 @@
     auto& b = *builder;
     switch (kind) {
         case SymbolDeclKind::GlobalVar:
-            return b.GlobalVar(source, symbol, b.ty.i32(), ast::StorageClass::kPrivate);
+            return b.GlobalVar(source, symbol, b.ty.i32(), ast::AddressSpace::kPrivate);
         case SymbolDeclKind::GlobalConst:
             return b.GlobalConst(source, symbol, b.ty.i32(), b.Expr(1_i));
         case SymbolDeclKind::Alias:
@@ -468,27 +468,27 @@
     switch (kind) {
         case SymbolUseKind::GlobalVarType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.GlobalVar(b.Sym(), node, ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), node, ast::AddressSpace::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarArrayElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.GlobalVar(b.Sym(), b.ty.array(node, 4_i), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.array(node, 4_i), ast::AddressSpace::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarArraySizeValue: {
             auto* node = b.Expr(source, symbol);
-            b.GlobalVar(b.Sym(), b.ty.array(b.ty.i32(), node), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.array(b.ty.i32(), node), ast::AddressSpace::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarVectorElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.GlobalVar(b.Sym(), b.ty.vec3(node), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.vec3(node), ast::AddressSpace::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarMatrixElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.GlobalVar(b.Sym(), b.ty.mat3x4(node), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.mat3x4(node), ast::AddressSpace::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarSampledTexElemType: {
@@ -503,7 +503,7 @@
         }
         case SymbolUseKind::GlobalVarValue: {
             auto* node = b.Expr(source, symbol);
-            b.GlobalVar(b.Sym(), b.ty.i32(), ast::StorageClass::kPrivate, node);
+            b.GlobalVar(b.Sym(), b.ty.i32(), ast::AddressSpace::kPrivate, node);
             return node;
         }
         case SymbolUseKind::GlobalConstType: {
@@ -724,7 +724,7 @@
              Block(Assign(Expr(Source{{12, 34}}, "G"), 3.14_f)),
          });
 
-    GlobalVar(Source{{56, 78}}, "G", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar(Source{{56, 78}}, "G", ty.f32(), ast::AddressSpace::kPrivate, Expr(2.1_f));
 
     Build();
 }
@@ -1207,7 +1207,7 @@
     const auto type_sym = Sym("TYPE");
     const auto func_sym = Sym("FUNC");
 
-    const auto* value_decl = GlobalVar(value_sym, ty.i32(), ast::StorageClass::kPrivate);
+    const auto* value_decl = GlobalVar(value_sym, ty.i32(), ast::AddressSpace::kPrivate);
     const auto* type_decl = Alias(type_sym, ty.i32());
     const auto* func_decl = Func(func_sym, utils::Empty, ty.void_(), utils::Empty);
 
@@ -1229,7 +1229,10 @@
 #define F add_use(func_decl, Expr(func_sym), __LINE__, "F()")
 
     Alias(Sym(), T);
-    Structure(Sym(), utils::Vector{Member(Sym(), T)});
+    Structure(Sym(),  //
+              utils::Vector{
+                  Member(Sym(), T, utils::Vector{MemberAlign(V)})  //
+              });
     GlobalVar(Sym(), T, V);
     GlobalConst(Sym(), T, V);
     Func(Sym(),                           //
@@ -1275,7 +1278,7 @@
     GlobalVar(Sym(), ty.array(T, V, 4));
     GlobalVar(Sym(), ty.vec3(T));
     GlobalVar(Sym(), ty.mat3x2(T));
-    GlobalVar(Sym(), ty.pointer(T, ast::StorageClass::kPrivate));
+    GlobalVar(Sym(), ty.pointer(T, ast::AddressSpace::kPrivate));
     GlobalVar(Sym(), ty.sampled_texture(ast::TextureDimension::k2d, T));
     GlobalVar(Sym(), ty.depth_texture(ast::TextureDimension::k2d));
     GlobalVar(Sym(), ty.depth_multisampled_texture(ast::TextureDimension::k2d));
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 2179f68..2a9f25b 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -453,25 +453,25 @@
     // enable chromium_experimental_push_constant;
     // var<push_constant> a : u32;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar("a", ty.u32(), ast::StorageClass::kPushConstant);
+    GlobalVar("a", ty.u32(), ast::AddressSpace::kPushConstant);
 
     EXPECT_TRUE(r()->Resolve());
 }
 
 TEST_F(ResolverEntryPointValidationTest, PushConstantDisallowedWithoutEnable) {
     // var<push_constant> a : u32;
-    GlobalVar(Source{{1, 2}}, "a", ty.u32(), ast::StorageClass::kPushConstant);
+    GlobalVar(Source{{1, 2}}, "a", ty.u32(), ast::AddressSpace::kPushConstant);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "1:2 error: use of variable storage class 'push_constant' requires enabling "
+              "1:2 error: use of variable address space 'push_constant' requires enabling "
               "extension 'chromium_experimental_push_constant'");
 }
 
-TEST_F(ResolverEntryPointValidationTest, PushConstantAllowedWithIgnoreStorageClassAttribute) {
-    // var<push_constant> a : u32; // With ast::DisabledValidation::kIgnoreStorageClass
-    GlobalVar("a", ty.u32(), ast::StorageClass::kPushConstant,
-              utils::Vector{Disable(ast::DisabledValidation::kIgnoreStorageClass)});
+TEST_F(ResolverEntryPointValidationTest, PushConstantAllowedWithIgnoreAddressSpaceAttribute) {
+    // var<push_constant> a : u32; // With ast::DisabledValidation::kIgnoreAddressSpace
+    GlobalVar("a", ty.u32(), ast::AddressSpace::kPushConstant,
+              utils::Vector{Disable(ast::DisabledValidation::kIgnoreAddressSpace)});
 
     EXPECT_TRUE(r()->Resolve());
 }
@@ -483,7 +483,7 @@
     //   _ = a;
     // }
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar("a", ty.u32(), ast::StorageClass::kPushConstant);
+    GlobalVar("a", ty.u32(), ast::AddressSpace::kPushConstant);
 
     Func("main", {}, ty.void_(), utils::Vector{Assign(Phony(), "a")},
          utils::Vector{Stage(ast::PipelineStage::kCompute),
@@ -501,8 +501,8 @@
     //   _ = b;
     // }
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{1, 2}}, "a", ty.u32(), ast::StorageClass::kPushConstant);
-    GlobalVar(Source{{3, 4}}, "b", ty.u32(), ast::StorageClass::kPushConstant);
+    GlobalVar(Source{{1, 2}}, "a", ty.u32(), ast::AddressSpace::kPushConstant);
+    GlobalVar(Source{{3, 4}}, "b", ty.u32(), ast::AddressSpace::kPushConstant);
 
     Func(Source{{5, 6}}, "main", {}, ty.void_(),
          utils::Vector{Assign(Phony(), "a"), Assign(Phony(), "b")},
@@ -532,8 +532,8 @@
     //   uses_b();
     // }
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{1, 2}}, "a", ty.u32(), ast::StorageClass::kPushConstant);
-    GlobalVar(Source{{3, 4}}, "b", ty.u32(), ast::StorageClass::kPushConstant);
+    GlobalVar(Source{{1, 2}}, "a", ty.u32(), ast::AddressSpace::kPushConstant);
+    GlobalVar(Source{{3, 4}}, "b", ty.u32(), ast::AddressSpace::kPushConstant);
 
     Func(Source{{5, 6}}, "uses_a", {}, ty.void_(), utils::Vector{Assign(Phony(), "a")});
     Func(Source{{7, 8}}, "uses_b", {}, ty.void_(), utils::Vector{Assign(Phony(), "b")});
@@ -565,8 +565,8 @@
     //   _ = a;
     // }
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar("a", ty.u32(), ast::StorageClass::kPushConstant);
-    GlobalVar("b", ty.u32(), ast::StorageClass::kPushConstant);
+    GlobalVar("a", ty.u32(), ast::AddressSpace::kPushConstant);
+    GlobalVar("b", ty.u32(), ast::AddressSpace::kPushConstant);
 
     Func("uses_a", {}, ty.void_(), utils::Vector{Assign(Phony(), "a")},
          utils::Vector{Stage(ast::PipelineStage::kCompute),
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index 5d88258..2fddc09 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -39,7 +39,7 @@
 TEST_F(ResolverFunctionValidationTest, ParameterMayShadowGlobal) {
     // var<private> common_name : f32;
     // fn func(common_name : f32) { }
-    GlobalVar("common_name", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("common_name", ty.f32(), ast::AddressSpace::kPrivate);
     Func("func", utils::Vector{Param("common_name", ty.f32())}, ty.void_(), utils::Empty);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -388,7 +388,7 @@
          utils::Vector{
              Return(1_i),
          });
-    GlobalVar("x", Call(Source{{12, 34}}, "F"), ast::StorageClass::kPrivate);
+    GlobalVar("x", Call(Source{{12, 34}}, "F"), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(12:34 error: functions cannot be called at module-scope)");
@@ -771,7 +771,7 @@
     // var<private> x = 64i;
     // @compute @workgroup_size(x)
     // fn main() {}
-    GlobalVar("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64_i));
+    GlobalVar("x", ty.i32(), ast::AddressSpace::kPrivate, Expr(64_i));
     Func("main", utils::Empty, ty.void_(), utils::Empty,
          utils::Vector{
              Stage(ast::PipelineStage::kCompute),
@@ -830,7 +830,7 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_NonPlain) {
-    auto* ret_type = ty.pointer(Source{{12, 34}}, ty.i32(), ast::StorageClass::kFunction);
+    auto* ret_type = ty.pointer(Source{{12, 34}}, ty.i32(), ast::AddressSpace::kFunction);
     Func("f", utils::Empty, ret_type, utils::Empty);
 
     EXPECT_FALSE(r()->Resolve());
@@ -939,16 +939,16 @@
 }
 
 struct TestParams {
-    ast::StorageClass storage_class;
+    ast::AddressSpace address_space;
     bool should_pass;
 };
 
 struct TestWithParams : ResolverTestWithParam<TestParams> {};
 
 using ResolverFunctionParameterValidationTest = TestWithParams;
-TEST_P(ResolverFunctionParameterValidationTest, StorageClass) {
+TEST_P(ResolverFunctionParameterValidationTest, AddressSpace) {
     auto& param = GetParam();
-    auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.storage_class);
+    auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.address_space);
     auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
     Func("f", utils::Vector{arg}, ty.void_(), utils::Empty);
 
@@ -956,23 +956,23 @@
         ASSERT_TRUE(r()->Resolve()) << r()->error();
     } else {
         std::stringstream ss;
-        ss << param.storage_class;
+        ss << param.address_space;
         EXPECT_FALSE(r()->Resolve());
         EXPECT_EQ(r()->error(), "12:34 error: function parameter of pointer type cannot be in '" +
-                                    ss.str() + "' storage class");
+                                    ss.str() + "' address space");
     }
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTest,
                          ResolverFunctionParameterValidationTest,
-                         testing::Values(TestParams{ast::StorageClass::kNone, false},
-                                         TestParams{ast::StorageClass::kIn, false},
-                                         TestParams{ast::StorageClass::kOut, false},
-                                         TestParams{ast::StorageClass::kUniform, false},
-                                         TestParams{ast::StorageClass::kWorkgroup, true},
-                                         TestParams{ast::StorageClass::kHandle, false},
-                                         TestParams{ast::StorageClass::kStorage, false},
-                                         TestParams{ast::StorageClass::kPrivate, true},
-                                         TestParams{ast::StorageClass::kFunction, true}));
+                         testing::Values(TestParams{ast::AddressSpace::kNone, false},
+                                         TestParams{ast::AddressSpace::kIn, false},
+                                         TestParams{ast::AddressSpace::kOut, false},
+                                         TestParams{ast::AddressSpace::kUniform, false},
+                                         TestParams{ast::AddressSpace::kWorkgroup, true},
+                                         TestParams{ast::AddressSpace::kHandle, false},
+                                         TestParams{ast::AddressSpace::kStorage, false},
+                                         TestParams{ast::AddressSpace::kPrivate, true},
+                                         TestParams{ast::AddressSpace::kFunction, true}));
 
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index bba156b..bd8f3ca 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -28,14 +28,14 @@
 TEST_F(ResolverHostShareableValidationTest, BoolMember) {
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.bool_())});
 
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
 12:34 note: while analysing structure member S.x
 56:78 note: while instantiating 'var' g)");
 }
@@ -43,14 +43,14 @@
 TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
 
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'vec3<bool>' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(56:78 error: Type 'vec3<bool>' cannot be used in address space 'storage' as it is non-host-shareable
 12:34 note: while analysing structure member S.x
 56:78 note: while instantiating 'var' g)");
 }
@@ -59,14 +59,14 @@
     auto* a1 = Alias("a1", ty.bool_());
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.Of(a1))});
     auto* a2 = Alias("a2", ty.Of(s));
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
 12:34 note: while analysing structure member S.x
 56:78 note: while instantiating 'var' g)");
 }
@@ -78,14 +78,14 @@
 
     auto* s = Structure("S", utils::Vector{Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
-    GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
-        R"(9:10 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+        R"(9:10 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
 1:2 note: while analysing structure member I1.x
 3:4 note: while analysing structure member I2.y
 5:6 note: while analysing structure member I3.z
@@ -117,7 +117,7 @@
 
     auto* s = Structure("S", utils::Vector{Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
-    GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
               Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/increment_decrement_validation_test.cc b/src/tint/resolver/increment_decrement_validation_test.cc
index 7368b6d..e822de3 100644
--- a/src/tint/resolver/increment_decrement_validation_test.cc
+++ b/src/tint/resolver/increment_decrement_validation_test.cc
@@ -64,8 +64,8 @@
     // var a : i32;
     // let b : ptr<function,i32> = &a;
     // *b++;
-    auto* var_a = Var("a", ty.i32(), ast::StorageClass::kFunction);
-    auto* var_b = Let("b", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(Expr("a")));
+    auto* var_a = Var("a", ty.i32(), ast::AddressSpace::kFunction);
+    auto* var_b = Let("b", ty.pointer<i32>(ast::AddressSpace::kFunction), AddressOf(Expr("a")));
     WrapInFunction(var_a, var_b, Increment(Source{{12, 34}}, Deref("b")));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -127,7 +127,7 @@
 TEST_F(ResolverIncrementDecrementValidationTest, Atomic) {
     // var<workgroup> a : atomic<i32>;
     // a++;
-    GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::AddressSpace::kWorkgroup);
     WrapInFunction(Increment(Expr(Source{{56, 78}}, "a")));
 
     EXPECT_FALSE(r()->Resolve());
@@ -193,7 +193,7 @@
     // {
     //   a++;
     // }
-    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead,
               Group(0_a), Binding(0_a));
     WrapInFunction(Increment(Source{{56, 78}}, "a"));
 
diff --git a/src/tint/resolver/inferred_type_test.cc b/src/tint/resolver/inferred_type_test.cc
index 2521959..3d03c1a 100644
--- a/src/tint/resolver/inferred_type_test.cc
+++ b/src/tint/resolver/inferred_type_test.cc
@@ -97,7 +97,7 @@
 
     // var a = <type constructor>;
     auto* ctor_expr = params.create_value(*this, 0);
-    auto* var = GlobalVar("a", ast::StorageClass::kPrivate, ctor_expr);
+    auto* var = GlobalVar("a", ast::AddressSpace::kPrivate, ctor_expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
@@ -124,7 +124,7 @@
 
     // var a = <type constructor>;
     auto* ctor_expr = params.create_value(*this, 0);
-    auto* var = Var("a", ast::StorageClass::kFunction, ctor_expr);
+    auto* var = Var("a", ast::AddressSpace::kFunction, ctor_expr);
     WrapInFunction(var);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -139,7 +139,7 @@
         create<sem::Array>(create<sem::U32>(), sem::ConstantArrayCount{10u}, 4u, 4u * 10u, 4u, 4u);
 
     auto* ctor_expr = Construct(type);
-    auto* var = Var("a", ast::StorageClass::kFunction, ctor_expr);
+    auto* var = Var("a", ast::AddressSpace::kFunction, ctor_expr);
     WrapInFunction(var);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -158,7 +158,7 @@
 
     auto* ctor_expr = Construct(ty.Of(str));
 
-    auto* var = Var("a", ast::StorageClass::kFunction, ctor_expr);
+    auto* var = Var("a", ast::AddressSpace::kFunction, ctor_expr);
     WrapInFunction(var);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 8533940..0eaaa37 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -27,6 +27,7 @@
 #include "src/tint/sem/atomic.h"
 #include "src/tint/sem/depth_multisampled_texture.h"
 #include "src/tint/sem/depth_texture.h"
+#include "src/tint/sem/evaluation_stage.h"
 #include "src/tint/sem/external_texture.h"
 #include "src/tint/sem/multisampled_texture.h"
 #include "src/tint/sem/pipeline_stage_set.h"
@@ -195,8 +196,14 @@
                TemplateState& t,
                const Matchers& m,
                const OverloadInfo* o,
-               MatcherIndex const* matcher_indices)
-        : builder(b), templates(t), matchers(m), overload(o), matcher_indices_(matcher_indices) {}
+               MatcherIndex const* matcher_indices,
+               sem::EvaluationStage s)
+        : builder(b),
+          templates(t),
+          matchers(m),
+          overload(o),
+          earliest_eval_stage(s),
+          matcher_indices_(matcher_indices) {}
 
     /// The program builder
     ProgramBuilder& builder;
@@ -206,6 +213,8 @@
     Matchers const& matchers;
     /// The current overload being evaluated
     OverloadInfo const* overload;
+    /// The earliest evaluation stage of the builtin call
+    sem::EvaluationStage earliest_eval_stage;
 
     /// Type uses the next TypeMatcher from the matcher indices to match the type
     /// `ty`. If the type matches, the canonical expected type is returned. If the
@@ -321,7 +330,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 using TexelFormat = ast::TexelFormat;
 using Access = ast::Access;
-using StorageClass = ast::StorageClass;
+using AddressSpace = ast::AddressSpace;
 using ParameterUsage = sem::ParameterUsage;
 using PipelineStage = ast::PipelineStage;
 
@@ -340,7 +349,7 @@
 // An enum set of OverloadFlag, used by OperatorInfo
 using OverloadFlags = utils::EnumSet<OverloadFlag>;
 
-bool match_bool(const sem::Type* ty) {
+bool match_bool(MatchState&, const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::Bool>();
 }
 
@@ -348,16 +357,18 @@
     return state.builder.create<sem::AbstractFloat>();
 }
 
-bool match_fa(const sem::Type* ty) {
-    return ty->IsAnyOf<Any, sem::AbstractNumeric>();
+bool match_fa(MatchState& state, const sem::Type* ty) {
+    return (state.earliest_eval_stage == sem::EvaluationStage::kConstant) &&
+           ty->IsAnyOf<Any, sem::AbstractNumeric>();
 }
 
 const sem::AbstractInt* build_ia(MatchState& state) {
     return state.builder.create<sem::AbstractInt>();
 }
 
-bool match_ia(const sem::Type* ty) {
-    return ty->IsAnyOf<Any, sem::AbstractInt>();
+bool match_ia(MatchState& state, const sem::Type* ty) {
+    return (state.earliest_eval_stage == sem::EvaluationStage::kConstant) &&
+           ty->IsAnyOf<Any, sem::AbstractInt>();
 }
 
 const sem::Bool* build_bool(MatchState& state) {
@@ -368,7 +379,7 @@
     return state.builder.create<sem::F16>();
 }
 
-bool match_f16(const sem::Type* ty) {
+bool match_f16(MatchState&, const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::F16, sem::AbstractNumeric>();
 }
 
@@ -376,7 +387,7 @@
     return state.builder.create<sem::F32>();
 }
 
-bool match_f32(const sem::Type* ty) {
+bool match_f32(MatchState&, const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::F32, sem::AbstractNumeric>();
 }
 
@@ -384,7 +395,7 @@
     return state.builder.create<sem::I32>();
 }
 
-bool match_i32(const sem::Type* ty) {
+bool match_i32(MatchState&, const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::I32, sem::AbstractInt>();
 }
 
@@ -392,11 +403,11 @@
     return state.builder.create<sem::U32>();
 }
 
-bool match_u32(const sem::Type* ty) {
+bool match_u32(MatchState&, const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::U32, sem::AbstractInt>();
 }
 
-bool match_vec(const sem::Type* ty, Number& N, const sem::Type*& T) {
+bool match_vec(MatchState&, const sem::Type* ty, Number& N, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         N = Number::any;
         T = ty;
@@ -412,7 +423,7 @@
 }
 
 template <uint32_t N>
-bool match_vec(const sem::Type* ty, const sem::Type*& T) {
+bool match_vec(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
         return true;
@@ -444,7 +455,7 @@
 constexpr auto build_vec3 = build_vec<3>;
 constexpr auto build_vec4 = build_vec<4>;
 
-bool match_mat(const sem::Type* ty, Number& M, Number& N, const sem::Type*& T) {
+bool match_mat(MatchState&, const sem::Type* ty, Number& M, Number& N, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         M = Number::any;
         N = Number::any;
@@ -461,7 +472,7 @@
 }
 
 template <uint32_t C, uint32_t R>
-bool match_mat(const sem::Type* ty, const sem::Type*& T) {
+bool match_mat(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
         return true;
@@ -506,7 +517,7 @@
 constexpr auto match_mat4x3 = match_mat<4, 3>;
 constexpr auto match_mat4x4 = match_mat<4, 4>;
 
-bool match_array(const sem::Type* ty, const sem::Type*& T) {
+bool match_array(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
         return true;
@@ -530,7 +541,7 @@
                                             /* stride_implicit */ 0u);
 }
 
-bool match_ptr(const sem::Type* ty, Number& S, const sem::Type*& T, Number& A) {
+bool match_ptr(MatchState&, const sem::Type* ty, Number& S, const sem::Type*& T, Number& A) {
     if (ty->Is<Any>()) {
         S = Number::any;
         T = ty;
@@ -539,7 +550,7 @@
     }
 
     if (auto* p = ty->As<sem::Pointer>()) {
-        S = Number(static_cast<uint32_t>(p->StorageClass()));
+        S = Number(static_cast<uint32_t>(p->AddressSpace()));
         T = p->StoreType();
         A = Number(static_cast<uint32_t>(p->Access()));
         return true;
@@ -548,11 +559,11 @@
 }
 
 const sem::Pointer* build_ptr(MatchState& state, Number S, const sem::Type* T, Number& A) {
-    return state.builder.create<sem::Pointer>(T, static_cast<ast::StorageClass>(S.Value()),
+    return state.builder.create<sem::Pointer>(T, static_cast<ast::AddressSpace>(S.Value()),
                                               static_cast<ast::Access>(A.Value()));
 }
 
-bool match_atomic(const sem::Type* ty, const sem::Type*& T) {
+bool match_atomic(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
         return true;
@@ -569,7 +580,7 @@
     return state.builder.create<sem::Atomic>(T);
 }
 
-bool match_sampler(const sem::Type* ty) {
+bool match_sampler(MatchState&, const sem::Type* ty) {
     if (ty->Is<Any>()) {
         return true;
     }
@@ -580,7 +591,7 @@
     return state.builder.create<sem::Sampler>(ast::SamplerKind::kSampler);
 }
 
-bool match_sampler_comparison(const sem::Type* ty) {
+bool match_sampler_comparison(MatchState&, const sem::Type* ty) {
     if (ty->Is<Any>()) {
         return true;
     }
@@ -592,7 +603,10 @@
     return state.builder.create<sem::Sampler>(ast::SamplerKind::kComparisonSampler);
 }
 
-bool match_texture(const sem::Type* ty, ast::TextureDimension dim, const sem::Type*& T) {
+bool match_texture(MatchState&,
+                   const sem::Type* ty,
+                   ast::TextureDimension dim,
+                   const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
         return true;
@@ -609,8 +623,9 @@
 #define JOIN(a, b) a##b
 
 #define DECLARE_SAMPLED_TEXTURE(suffix, dim)                                      \
-    bool JOIN(match_texture_, suffix)(const sem::Type* ty, const sem::Type*& T) { \
-        return match_texture(ty, dim, T);                                         \
+    bool JOIN(match_texture_, suffix)(MatchState & state, const sem::Type* ty,    \
+                                      const sem::Type*& T) {                      \
+        return match_texture(state, ty, dim, T);                                  \
     }                                                                             \
     const sem::SampledTexture* JOIN(build_texture_, suffix)(MatchState & state,   \
                                                             const sem::Type* T) { \
@@ -625,7 +640,8 @@
 DECLARE_SAMPLED_TEXTURE(cube_array, ast::TextureDimension::kCubeArray)
 #undef DECLARE_SAMPLED_TEXTURE
 
-bool match_texture_multisampled(const sem::Type* ty,
+bool match_texture_multisampled(MatchState&,
+                                const sem::Type* ty,
                                 ast::TextureDimension dim,
                                 const sem::Type*& T) {
     if (ty->Is<Any>()) {
@@ -641,31 +657,32 @@
     return false;
 }
 
-#define DECLARE_MULTISAMPLED_TEXTURE(suffix, dim)                                              \
-    bool JOIN(match_texture_multisampled_, suffix)(const sem::Type* ty, const sem::Type*& T) { \
-        return match_texture_multisampled(ty, dim, T);                                         \
-    }                                                                                          \
-    const sem::MultisampledTexture* JOIN(build_texture_multisampled_, suffix)(                 \
-        MatchState & state, const sem::Type* T) {                                              \
-        return state.builder.create<sem::MultisampledTexture>(dim, T);                         \
+#define DECLARE_MULTISAMPLED_TEXTURE(suffix, dim)                                           \
+    bool JOIN(match_texture_multisampled_, suffix)(MatchState & state, const sem::Type* ty, \
+                                                   const sem::Type*& T) {                   \
+        return match_texture_multisampled(state, ty, dim, T);                               \
+    }                                                                                       \
+    const sem::MultisampledTexture* JOIN(build_texture_multisampled_, suffix)(              \
+        MatchState & state, const sem::Type* T) {                                           \
+        return state.builder.create<sem::MultisampledTexture>(dim, T);                      \
     }
 
 DECLARE_MULTISAMPLED_TEXTURE(2d, ast::TextureDimension::k2d)
 #undef DECLARE_MULTISAMPLED_TEXTURE
 
-bool match_texture_depth(const sem::Type* ty, ast::TextureDimension dim) {
+bool match_texture_depth(MatchState&, const sem::Type* ty, ast::TextureDimension dim) {
     if (ty->Is<Any>()) {
         return true;
     }
     return ty->Is([&](const sem::DepthTexture* t) { return t->dim() == dim; });
 }
 
-#define DECLARE_DEPTH_TEXTURE(suffix, dim)                                            \
-    bool JOIN(match_texture_depth_, suffix)(const sem::Type* ty) {                    \
-        return match_texture_depth(ty, dim);                                          \
-    }                                                                                 \
-    const sem::DepthTexture* JOIN(build_texture_depth_, suffix)(MatchState & state) { \
-        return state.builder.create<sem::DepthTexture>(dim);                          \
+#define DECLARE_DEPTH_TEXTURE(suffix, dim)                                             \
+    bool JOIN(match_texture_depth_, suffix)(MatchState & state, const sem::Type* ty) { \
+        return match_texture_depth(state, ty, dim);                                    \
+    }                                                                                  \
+    const sem::DepthTexture* JOIN(build_texture_depth_, suffix)(MatchState & state) {  \
+        return state.builder.create<sem::DepthTexture>(dim);                           \
     }
 
 DECLARE_DEPTH_TEXTURE(2d, ast::TextureDimension::k2d)
@@ -674,7 +691,7 @@
 DECLARE_DEPTH_TEXTURE(cube_array, ast::TextureDimension::kCubeArray)
 #undef DECLARE_DEPTH_TEXTURE
 
-bool match_texture_depth_multisampled_2d(const sem::Type* ty) {
+bool match_texture_depth_multisampled_2d(MatchState&, const sem::Type* ty) {
     if (ty->Is<Any>()) {
         return true;
     }
@@ -687,7 +704,11 @@
     return state.builder.create<sem::DepthMultisampledTexture>(ast::TextureDimension::k2d);
 }
 
-bool match_texture_storage(const sem::Type* ty, ast::TextureDimension dim, Number& F, Number& A) {
+bool match_texture_storage(MatchState&,
+                           const sem::Type* ty,
+                           ast::TextureDimension dim,
+                           Number& F,
+                           Number& A) {
     if (ty->Is<Any>()) {
         F = Number::any;
         A = Number::any;
@@ -704,8 +725,9 @@
 }
 
 #define DECLARE_STORAGE_TEXTURE(suffix, dim)                                                      \
-    bool JOIN(match_texture_storage_, suffix)(const sem::Type* ty, Number& F, Number& A) {        \
-        return match_texture_storage(ty, dim, F, A);                                              \
+    bool JOIN(match_texture_storage_, suffix)(MatchState & state, const sem::Type* ty, Number& F, \
+                                              Number& A) {                                        \
+        return match_texture_storage(state, ty, dim, F, A);                                       \
     }                                                                                             \
     const sem::StorageTexture* JOIN(build_texture_storage_, suffix)(MatchState & state, Number F, \
                                                                     Number A) {                   \
@@ -721,7 +743,7 @@
 DECLARE_STORAGE_TEXTURE(3d, ast::TextureDimension::k3d)
 #undef DECLARE_STORAGE_TEXTURE
 
-bool match_texture_external(const sem::Type* ty) {
+bool match_texture_external(MatchState&, const sem::Type* ty) {
     return ty->IsAnyOf<Any, sem::ExternalTexture>();
 }
 
@@ -732,14 +754,14 @@
 // Builtin types starting with a _ prefix cannot be declared in WGSL, so they
 // can only be used as return types. Because of this, they must only match Any,
 // which is used as the return type matcher.
-bool match_modf_result(const sem::Type* ty, const sem::Type*& T) {
+bool match_modf_result(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (!ty->Is<Any>()) {
         return false;
     }
     T = ty;
     return true;
 }
-bool match_modf_result_vec(const sem::Type* ty, Number& N, const sem::Type*& T) {
+bool match_modf_result_vec(MatchState&, const sem::Type* ty, Number& N, const sem::Type*& T) {
     if (!ty->Is<Any>()) {
         return false;
     }
@@ -747,14 +769,14 @@
     T = ty;
     return true;
 }
-bool match_frexp_result(const sem::Type* ty, const sem::Type*& T) {
+bool match_frexp_result(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (!ty->Is<Any>()) {
         return false;
     }
     T = ty;
     return true;
 }
-bool match_frexp_result_vec(const sem::Type* ty, Number& N, const sem::Type*& T) {
+bool match_frexp_result_vec(MatchState&, const sem::Type* ty, Number& N, const sem::Type*& T) {
     if (!ty->Is<Any>()) {
         return false;
     }
@@ -763,7 +785,7 @@
     return true;
 }
 
-bool match_atomic_compare_exchange_result(const sem::Type* ty, const sem::Type*& T) {
+bool match_atomic_compare_exchange_result(MatchState&, const sem::Type* ty, const sem::Type*& T) {
     if (ty->Is<Any>()) {
         T = ty;
         return true;
@@ -970,6 +992,7 @@
 
     Builtin Lookup(sem::BuiltinType builtin_type,
                    utils::VectorRef<const sem::Type*> args,
+                   sem::EvaluationStage earliest_eval_stage,
                    const Source& source) override;
 
     UnaryOperator Lookup(ast::UnaryOp op, const sem::Type* arg, const Source& source) override;
@@ -1028,6 +1051,7 @@
     IntrinsicPrototype MatchIntrinsic(const IntrinsicInfo& intrinsic,
                                       const char* intrinsic_name,
                                       utils::VectorRef<const sem::Type*> args,
+                                      sem::EvaluationStage earliest_eval_stage,
                                       TemplateState templates,
                                       OnNoMatch on_no_match) const;
 
@@ -1040,6 +1064,7 @@
     /// @returns the evaluated Candidate information.
     Candidate ScoreOverload(const OverloadInfo* overload,
                             utils::VectorRef<const sem::Type*> args,
+                            sem::EvaluationStage earliest_eval_stage,
                             TemplateState templates) const;
 
     /// Performs overload resolution given the list of candidates, by ranking the conversions of
@@ -1063,7 +1088,8 @@
     /// @param matcher_indices pointer to a list of matcher indices
     MatchState Match(TemplateState& templates,
                      const OverloadInfo* overload,
-                     MatcherIndex const* matcher_indices) const;
+                     MatcherIndex const* matcher_indices,
+                     sem::EvaluationStage earliest_eval_stage) const;
 
     // Prints the overload for emitting diagnostics
     void PrintOverload(std::ostream& ss,
@@ -1129,6 +1155,7 @@
 
 Impl::Builtin Impl::Lookup(sem::BuiltinType builtin_type,
                            utils::VectorRef<const sem::Type*> args,
+                           sem::EvaluationStage earliest_eval_stage,
                            const Source& source) {
     const char* intrinsic_name = sem::str(builtin_type);
 
@@ -1147,7 +1174,7 @@
 
     // Resolve the intrinsic overload
     auto match = MatchIntrinsic(kBuiltins[static_cast<size_t>(builtin_type)], intrinsic_name, args,
-                                TemplateState{}, on_no_match);
+                                earliest_eval_stage, TemplateState{}, on_no_match);
     if (!match.overload) {
         return {};
     }
@@ -1158,7 +1185,7 @@
         params.Reserve(match.parameters.Length());
         for (auto& p : match.parameters) {
             params.Push(builder.create<sem::Parameter>(
-                nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::StorageClass::kNone,
+                nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::AddressSpace::kNone,
                 ast::Access::kUndefined, p.usage));
         }
         sem::PipelineStageSet supported_stages;
@@ -1213,7 +1240,7 @@
 
     // Resolve the intrinsic overload
     auto match = MatchIntrinsic(kUnaryOperators[intrinsic_index], intrinsic_name, args,
-                                TemplateState{}, on_no_match);
+                                sem::EvaluationStage::kConstant, TemplateState{}, on_no_match);
     if (!match.overload) {
         return {};
     }
@@ -1290,7 +1317,7 @@
 
     // Resolve the intrinsic overload
     auto match = MatchIntrinsic(kBinaryOperators[intrinsic_index], intrinsic_name, args,
-                                TemplateState{}, on_no_match);
+                                sem::EvaluationStage::kConstant, TemplateState{}, on_no_match);
     if (!match.overload) {
         return {};
     }
@@ -1345,7 +1372,7 @@
 
     // Resolve the intrinsic overload
     auto match = MatchIntrinsic(kConstructorsAndConverters[static_cast<size_t>(type)], name, args,
-                                templates, on_no_match);
+                                sem::EvaluationStage::kConstant, templates, on_no_match);
     if (!match.overload) {
         return {};
     }
@@ -1356,7 +1383,7 @@
         params.Reserve(match.parameters.Length());
         for (auto& p : match.parameters) {
             params.Push(builder.create<sem::Parameter>(
-                nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::StorageClass::kNone,
+                nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::AddressSpace::kNone,
                 ast::Access::kUndefined, p.usage));
         }
         auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
@@ -1371,7 +1398,7 @@
     // Conversion.
     auto* target = utils::GetOrCreate(converters, match, [&]() {
         auto param = builder.create<sem::Parameter>(
-            nullptr, 0u, match.parameters[0].type, ast::StorageClass::kNone,
+            nullptr, 0u, match.parameters[0].type, ast::AddressSpace::kNone,
             ast::Access::kUndefined, match.parameters[0].usage);
         auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
                                                         : sem::EvaluationStage::kRuntime;
@@ -1383,6 +1410,7 @@
 IntrinsicPrototype Impl::MatchIntrinsic(const IntrinsicInfo& intrinsic,
                                         const char* intrinsic_name,
                                         utils::VectorRef<const sem::Type*> args,
+                                        sem::EvaluationStage earliest_eval_stage,
                                         TemplateState templates,
                                         OnNoMatch on_no_match) const {
     size_t num_matched = 0;
@@ -1391,7 +1419,8 @@
     candidates.Reserve(intrinsic.num_overloads);
     for (size_t overload_idx = 0; overload_idx < static_cast<size_t>(intrinsic.num_overloads);
          overload_idx++) {
-        auto candidate = ScoreOverload(&intrinsic.overloads[overload_idx], args, templates);
+        auto candidate =
+            ScoreOverload(&intrinsic.overloads[overload_idx], args, earliest_eval_stage, templates);
         if (candidate.score == 0) {
             match_idx = overload_idx;
             num_matched++;
@@ -1423,7 +1452,8 @@
     const sem::Type* return_type = nullptr;
     if (auto* indices = match.overload->return_matcher_indices) {
         Any any;
-        return_type = Match(match.templates, match.overload, indices).Type(&any);
+        return_type =
+            Match(match.templates, match.overload, indices, earliest_eval_stage).Type(&any);
         if (!return_type) {
             TINT_ICE(Resolver, builder.Diagnostics()) << "MatchState.Match() returned null";
             return {};
@@ -1437,6 +1467,7 @@
 
 Impl::Candidate Impl::ScoreOverload(const OverloadInfo* overload,
                                     utils::VectorRef<const sem::Type*> args,
+                                    sem::EvaluationStage earliest_eval_stage,
                                     TemplateState templates) const {
     // Penalty weights for overload mismatching.
     // This scoring is used to order the suggested overloads in diagnostic on overload mismatch, and
@@ -1469,7 +1500,7 @@
     for (size_t p = 0; p < num_params; p++) {
         auto& parameter = overload->parameters[p];
         auto* indices = parameter.matcher_indices;
-        if (!Match(templates, overload, indices).Type(args[p]->UnwrapRef())) {
+        if (!Match(templates, overload, indices, earliest_eval_stage).Type(args[p]->UnwrapRef())) {
             score += kMismatchedParamTypePenalty;
         }
     }
@@ -1485,7 +1516,8 @@
             auto* matcher_index = &overload->template_types[ot].matcher_index;
             if (*matcher_index != kNoMatcher) {
                 if (auto* template_type = templates.Type(ot)) {
-                    if (auto* ty = Match(templates, overload, matcher_index).Type(template_type)) {
+                    if (auto* ty = Match(templates, overload, matcher_index, earliest_eval_stage)
+                                       .Type(template_type)) {
                         // Template type matched one of the types in the template type's matcher.
                         // Replace the template type with this type.
                         templates.SetType(ot, ty);
@@ -1507,7 +1539,9 @@
             if (*matcher_index != kNoMatcher) {
                 auto template_num = templates.Num(on);
                 if (!template_num.IsValid() ||
-                    !Match(templates, overload, matcher_index).Num(template_num).IsValid()) {
+                    !Match(templates, overload, matcher_index, earliest_eval_stage)
+                         .Num(template_num)
+                         .IsValid()) {
                     score += kMismatchedTemplateNumberPenalty;
                 }
             }
@@ -1521,7 +1555,8 @@
         for (size_t p = 0; p < num_params; p++) {
             auto& parameter = overload->parameters[p];
             auto* indices = parameter.matcher_indices;
-            auto* ty = Match(templates, overload, indices).Type(args[p]->UnwrapRef());
+            auto* ty =
+                Match(templates, overload, indices, earliest_eval_stage).Type(args[p]->UnwrapRef());
             parameters.Emplace(ty, parameter.usage);
         }
     }
@@ -1596,8 +1631,9 @@
 
 MatchState Impl::Match(TemplateState& templates,
                        const OverloadInfo* overload,
-                       MatcherIndex const* matcher_indices) const {
-    return MatchState(builder, templates, matchers, overload, matcher_indices);
+                       MatcherIndex const* matcher_indices,
+                       sem::EvaluationStage earliest_eval_stage) const {
+    return MatchState(builder, templates, matchers, overload, matcher_indices, earliest_eval_stage);
 }
 
 void Impl::PrintOverload(std::ostream& ss,
@@ -1605,6 +1641,8 @@
                          const char* intrinsic_name) const {
     TemplateState templates;
 
+    auto earliest_eval_stage = sem::EvaluationStage::kConstant;
+
     ss << intrinsic_name << "(";
     for (size_t p = 0; p < overload->num_parameters; p++) {
         auto& parameter = overload->parameters[p];
@@ -1615,13 +1653,13 @@
             ss << sem::str(parameter.usage) << ": ";
         }
         auto* indices = parameter.matcher_indices;
-        ss << Match(templates, overload, indices).TypeName();
+        ss << Match(templates, overload, indices, earliest_eval_stage).TypeName();
     }
     ss << ")";
     if (overload->return_matcher_indices) {
         ss << " -> ";
         auto* indices = overload->return_matcher_indices;
-        ss << Match(templates, overload, indices).TypeName();
+        ss << Match(templates, overload, indices, earliest_eval_stage).TypeName();
     }
 
     bool first = true;
@@ -1635,7 +1673,7 @@
             separator();
             ss << template_type.name;
             auto* index = &template_type.matcher_index;
-            ss << " is " << Match(templates, overload, index).TypeName();
+            ss << " is " << Match(templates, overload, index, earliest_eval_stage).TypeName();
         }
     }
     for (size_t i = 0; i < overload->num_template_numbers; i++) {
@@ -1644,7 +1682,7 @@
             separator();
             ss << template_number.name;
             auto* index = &template_number.matcher_index;
-            ss << " is " << Match(templates, overload, index).NumName();
+            ss << " is " << Match(templates, overload, index, earliest_eval_stage).NumName();
         }
     }
 }
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index b36f8d8..b27f5ee 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -84,10 +84,17 @@
     /// if the builtin was not found.
     /// @param type the builtin type
     /// @param args the argument types passed to the builtin function
+    /// @param earliest_eval_stage the the earliest evaluation stage that a call to
+    ///        the builtin can be made. This can alter the overloads considered.
+    ///        For example, if the earliest evaluation stage is
+    ///        `sem::EvaluationStage::kRuntime`, then only concrete argument types
+    ///        will be considered, as all abstract-numerics will have been materialized
+    ///        after shader creation time (sem::EvaluationStage::kConstant).
     /// @param source the source of the builtin call
     /// @return the semantic builtin if found, otherwise nullptr
     virtual Builtin Lookup(sem::BuiltinType type,
                            utils::VectorRef<const sem::Type*> args,
+                           sem::EvaluationStage earliest_eval_stage,
                            const Source& source) = 0;
 
     /// Lookup looks for the unary op overload with the given signature, raising an error
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index dfe2574..a589e5a 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -38,7 +38,7 @@
 };
 
 const sem::Type* Bool::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_bool(ty)) {
+  if (!match_bool(state, ty)) {
     return nullptr;
   }
   return build_bool(state);
@@ -64,7 +64,7 @@
 };
 
 const sem::Type* Ia::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_ia(ty)) {
+  if (!match_ia(state, ty)) {
     return nullptr;
   }
   return build_ia(state);
@@ -92,7 +92,7 @@
 };
 
 const sem::Type* Fa::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_fa(ty)) {
+  if (!match_fa(state, ty)) {
     return nullptr;
   }
   return build_fa(state);
@@ -120,7 +120,7 @@
 };
 
 const sem::Type* I32::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_i32(ty)) {
+  if (!match_i32(state, ty)) {
     return nullptr;
   }
   return build_i32(state);
@@ -146,7 +146,7 @@
 };
 
 const sem::Type* U32::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_u32(ty)) {
+  if (!match_u32(state, ty)) {
     return nullptr;
   }
   return build_u32(state);
@@ -172,7 +172,7 @@
 };
 
 const sem::Type* F32::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_f32(ty)) {
+  if (!match_f32(state, ty)) {
     return nullptr;
   }
   return build_f32(state);
@@ -198,7 +198,7 @@
 };
 
 const sem::Type* F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_f16(ty)) {
+  if (!match_f16(state, ty)) {
     return nullptr;
   }
   return build_f16(state);
@@ -225,7 +225,7 @@
 
 const sem::Type* Vec2::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_vec2(ty, T)) {
+  if (!match_vec2(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -257,7 +257,7 @@
 
 const sem::Type* Vec3::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_vec3(ty, T)) {
+  if (!match_vec3(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -289,7 +289,7 @@
 
 const sem::Type* Vec4::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_vec4(ty, T)) {
+  if (!match_vec4(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -321,7 +321,7 @@
 
 const sem::Type* Mat2X2::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat2x2(ty, T)) {
+  if (!match_mat2x2(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -353,7 +353,7 @@
 
 const sem::Type* Mat2X3::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat2x3(ty, T)) {
+  if (!match_mat2x3(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -385,7 +385,7 @@
 
 const sem::Type* Mat2X4::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat2x4(ty, T)) {
+  if (!match_mat2x4(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -417,7 +417,7 @@
 
 const sem::Type* Mat3X2::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat3x2(ty, T)) {
+  if (!match_mat3x2(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -449,7 +449,7 @@
 
 const sem::Type* Mat3X3::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat3x3(ty, T)) {
+  if (!match_mat3x3(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -481,7 +481,7 @@
 
 const sem::Type* Mat3X4::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat3x4(ty, T)) {
+  if (!match_mat3x4(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -513,7 +513,7 @@
 
 const sem::Type* Mat4X2::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat4x2(ty, T)) {
+  if (!match_mat4x2(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -545,7 +545,7 @@
 
 const sem::Type* Mat4X3::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat4x3(ty, T)) {
+  if (!match_mat4x3(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -577,7 +577,7 @@
 
 const sem::Type* Mat4X4::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_mat4x4(ty, T)) {
+  if (!match_mat4x4(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -610,7 +610,7 @@
 const sem::Type* Vec::Match(MatchState& state, const sem::Type* ty) const {
   Number N = Number::invalid;
   const sem::Type* T = nullptr;
-  if (!match_vec(ty, N, T)) {
+  if (!match_vec(state, ty, N, T)) {
     return nullptr;
   }
   N = state.Num(N);
@@ -651,7 +651,7 @@
   Number N = Number::invalid;
   Number M = Number::invalid;
   const sem::Type* T = nullptr;
-  if (!match_mat(ty, N, M, T)) {
+  if (!match_mat(state, ty, N, M, T)) {
     return nullptr;
   }
   N = state.Num(N);
@@ -697,7 +697,7 @@
   Number S = Number::invalid;
   const sem::Type* T = nullptr;
   Number A = Number::invalid;
-  if (!match_ptr(ty, S, T, A)) {
+  if (!match_ptr(state, ty, S, T, A)) {
     return nullptr;
   }
   S = state.Num(S);
@@ -739,7 +739,7 @@
 
 const sem::Type* Atomic::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_atomic(ty, T)) {
+  if (!match_atomic(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -771,7 +771,7 @@
 
 const sem::Type* Array::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_array(ty, T)) {
+  if (!match_array(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -802,7 +802,7 @@
 };
 
 const sem::Type* Sampler::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_sampler(ty)) {
+  if (!match_sampler(state, ty)) {
     return nullptr;
   }
   return build_sampler(state);
@@ -828,7 +828,7 @@
 };
 
 const sem::Type* SamplerComparison::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_sampler_comparison(ty)) {
+  if (!match_sampler_comparison(state, ty)) {
     return nullptr;
   }
   return build_sampler_comparison(state);
@@ -855,7 +855,7 @@
 
 const sem::Type* Texture1D::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_1d(ty, T)) {
+  if (!match_texture_1d(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -887,7 +887,7 @@
 
 const sem::Type* Texture2D::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_2d(ty, T)) {
+  if (!match_texture_2d(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -919,7 +919,7 @@
 
 const sem::Type* Texture2DArray::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_2d_array(ty, T)) {
+  if (!match_texture_2d_array(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -951,7 +951,7 @@
 
 const sem::Type* Texture3D::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_3d(ty, T)) {
+  if (!match_texture_3d(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -983,7 +983,7 @@
 
 const sem::Type* TextureCube::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_cube(ty, T)) {
+  if (!match_texture_cube(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -1015,7 +1015,7 @@
 
 const sem::Type* TextureCubeArray::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_cube_array(ty, T)) {
+  if (!match_texture_cube_array(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -1047,7 +1047,7 @@
 
 const sem::Type* TextureMultisampled2D::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_texture_multisampled_2d(ty, T)) {
+  if (!match_texture_multisampled_2d(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -1078,7 +1078,7 @@
 };
 
 const sem::Type* TextureDepth2D::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_texture_depth_2d(ty)) {
+  if (!match_texture_depth_2d(state, ty)) {
     return nullptr;
   }
   return build_texture_depth_2d(state);
@@ -1104,7 +1104,7 @@
 };
 
 const sem::Type* TextureDepth2DArray::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_texture_depth_2d_array(ty)) {
+  if (!match_texture_depth_2d_array(state, ty)) {
     return nullptr;
   }
   return build_texture_depth_2d_array(state);
@@ -1130,7 +1130,7 @@
 };
 
 const sem::Type* TextureDepthCube::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_texture_depth_cube(ty)) {
+  if (!match_texture_depth_cube(state, ty)) {
     return nullptr;
   }
   return build_texture_depth_cube(state);
@@ -1156,7 +1156,7 @@
 };
 
 const sem::Type* TextureDepthCubeArray::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_texture_depth_cube_array(ty)) {
+  if (!match_texture_depth_cube_array(state, ty)) {
     return nullptr;
   }
   return build_texture_depth_cube_array(state);
@@ -1182,7 +1182,7 @@
 };
 
 const sem::Type* TextureDepthMultisampled2D::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_texture_depth_multisampled_2d(ty)) {
+  if (!match_texture_depth_multisampled_2d(state, ty)) {
     return nullptr;
   }
   return build_texture_depth_multisampled_2d(state);
@@ -1210,7 +1210,7 @@
 const sem::Type* TextureStorage1D::Match(MatchState& state, const sem::Type* ty) const {
   Number F = Number::invalid;
   Number A = Number::invalid;
-  if (!match_texture_storage_1d(ty, F, A)) {
+  if (!match_texture_storage_1d(state, ty, F, A)) {
     return nullptr;
   }
   F = state.Num(F);
@@ -1248,7 +1248,7 @@
 const sem::Type* TextureStorage2D::Match(MatchState& state, const sem::Type* ty) const {
   Number F = Number::invalid;
   Number A = Number::invalid;
-  if (!match_texture_storage_2d(ty, F, A)) {
+  if (!match_texture_storage_2d(state, ty, F, A)) {
     return nullptr;
   }
   F = state.Num(F);
@@ -1286,7 +1286,7 @@
 const sem::Type* TextureStorage2DArray::Match(MatchState& state, const sem::Type* ty) const {
   Number F = Number::invalid;
   Number A = Number::invalid;
-  if (!match_texture_storage_2d_array(ty, F, A)) {
+  if (!match_texture_storage_2d_array(state, ty, F, A)) {
     return nullptr;
   }
   F = state.Num(F);
@@ -1324,7 +1324,7 @@
 const sem::Type* TextureStorage3D::Match(MatchState& state, const sem::Type* ty) const {
   Number F = Number::invalid;
   Number A = Number::invalid;
-  if (!match_texture_storage_3d(ty, F, A)) {
+  if (!match_texture_storage_3d(state, ty, F, A)) {
     return nullptr;
   }
   F = state.Num(F);
@@ -1360,7 +1360,7 @@
 };
 
 const sem::Type* TextureExternal::Match(MatchState& state, const sem::Type* ty) const {
-  if (!match_texture_external(ty)) {
+  if (!match_texture_external(state, ty)) {
     return nullptr;
   }
   return build_texture_external(state);
@@ -1387,7 +1387,7 @@
 
 const sem::Type* ModfResult::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_modf_result(ty, T)) {
+  if (!match_modf_result(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -1422,7 +1422,7 @@
 const sem::Type* ModfResultVec::Match(MatchState& state, const sem::Type* ty) const {
   Number N = Number::invalid;
   const sem::Type* T = nullptr;
-  if (!match_modf_result_vec(ty, N, T)) {
+  if (!match_modf_result_vec(state, ty, N, T)) {
     return nullptr;
   }
   N = state.Num(N);
@@ -1461,7 +1461,7 @@
 
 const sem::Type* FrexpResult::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_frexp_result(ty, T)) {
+  if (!match_frexp_result(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -1496,7 +1496,7 @@
 const sem::Type* FrexpResultVec::Match(MatchState& state, const sem::Type* ty) const {
   Number N = Number::invalid;
   const sem::Type* T = nullptr;
-  if (!match_frexp_result_vec(ty, N, T)) {
+  if (!match_frexp_result_vec(state, ty, N, T)) {
     return nullptr;
   }
   N = state.Num(N);
@@ -1535,7 +1535,7 @@
 
 const sem::Type* AtomicCompareExchangeResult::Match(MatchState& state, const sem::Type* ty) const {
   const sem::Type* T = nullptr;
-  if (!match_atomic_compare_exchange_result(ty, T)) {
+  if (!match_atomic_compare_exchange_result(state, ty, T)) {
     return nullptr;
   }
   T = state.Type(T);
@@ -1567,25 +1567,25 @@
 };
 
 const sem::Type* AbstractOrScalar::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_ia(ty)) {
+  if (match_ia(state, ty)) {
     return build_ia(state);
   }
-  if (match_fa(ty)) {
+  if (match_fa(state, ty)) {
     return build_fa(state);
   }
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
-  if (match_bool(ty)) {
+  if (match_bool(state, ty)) {
     return build_bool(state);
   }
   return nullptr;
@@ -1616,19 +1616,19 @@
 };
 
 const sem::Type* Scalar::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
-  if (match_bool(ty)) {
+  if (match_bool(state, ty)) {
     return build_bool(state);
   }
   return nullptr;
@@ -1659,16 +1659,16 @@
 };
 
 const sem::Type* ScalarNoF32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
-  if (match_bool(ty)) {
+  if (match_bool(state, ty)) {
     return build_bool(state);
   }
   return nullptr;
@@ -1699,16 +1699,16 @@
 };
 
 const sem::Type* ScalarNoF16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_bool(ty)) {
+  if (match_bool(state, ty)) {
     return build_bool(state);
   }
   return nullptr;
@@ -1739,16 +1739,16 @@
 };
 
 const sem::Type* ScalarNoI32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
-  if (match_bool(ty)) {
+  if (match_bool(state, ty)) {
     return build_bool(state);
   }
   return nullptr;
@@ -1779,16 +1779,16 @@
 };
 
 const sem::Type* ScalarNoU32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
-  if (match_bool(ty)) {
+  if (match_bool(state, ty)) {
     return build_bool(state);
   }
   return nullptr;
@@ -1819,16 +1819,16 @@
 };
 
 const sem::Type* ScalarNoBool::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -1859,22 +1859,22 @@
 };
 
 const sem::Type* FiaFiu32F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_ia(ty)) {
+  if (match_ia(state, ty)) {
     return build_ia(state);
   }
-  if (match_fa(ty)) {
+  if (match_fa(state, ty)) {
     return build_fa(state);
   }
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -1905,19 +1905,19 @@
 };
 
 const sem::Type* FiaFi32F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_ia(ty)) {
+  if (match_ia(state, ty)) {
     return build_ia(state);
   }
-  if (match_fa(ty)) {
+  if (match_fa(state, ty)) {
     return build_fa(state);
   }
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -1948,19 +1948,19 @@
 };
 
 const sem::Type* FiaFiu32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_ia(ty)) {
+  if (match_ia(state, ty)) {
     return build_ia(state);
   }
-  if (match_fa(ty)) {
+  if (match_fa(state, ty)) {
     return build_fa(state);
   }
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
   return nullptr;
@@ -1991,10 +1991,10 @@
 };
 
 const sem::Type* FaF32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_fa(ty)) {
+  if (match_fa(state, ty)) {
     return build_fa(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
   return nullptr;
@@ -2025,13 +2025,13 @@
 };
 
 const sem::Type* FaF32F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_fa(ty)) {
+  if (match_fa(state, ty)) {
     return build_fa(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -2062,13 +2062,13 @@
 };
 
 const sem::Type* IaIu32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_ia(ty)) {
+  if (match_ia(state, ty)) {
     return build_ia(state);
   }
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
   return nullptr;
@@ -2099,16 +2099,16 @@
 };
 
 const sem::Type* Fiu32F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -2139,13 +2139,13 @@
 };
 
 const sem::Type* Fiu32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
   return nullptr;
@@ -2176,13 +2176,13 @@
 };
 
 const sem::Type* Fi32F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -2213,10 +2213,10 @@
 };
 
 const sem::Type* Fi32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
   return nullptr;
@@ -2247,10 +2247,10 @@
 };
 
 const sem::Type* F32F16::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_f32(ty)) {
+  if (match_f32(state, ty)) {
     return build_f32(state);
   }
-  if (match_f16(ty)) {
+  if (match_f16(state, ty)) {
     return build_f16(state);
   }
   return nullptr;
@@ -2281,10 +2281,10 @@
 };
 
 const sem::Type* Iu32::Match(MatchState& state, const sem::Type* ty) const {
-  if (match_i32(ty)) {
+  if (match_i32(state, ty)) {
     return build_i32(state);
   }
-  if (match_u32(ty)) {
+  if (match_u32(state, ty)) {
     return build_u32(state);
   }
   return nullptr;
@@ -2457,10 +2457,10 @@
 };
 
 Number FunctionPrivateWorkgroup::Match(MatchState&, Number number) const {
-  switch (static_cast<StorageClass>(number.Value())) {
-    case StorageClass::kFunction:
-    case StorageClass::kPrivate:
-    case StorageClass::kWorkgroup:
+  switch (static_cast<AddressSpace>(number.Value())) {
+    case AddressSpace::kFunction:
+    case AddressSpace::kPrivate:
+    case AddressSpace::kWorkgroup:
       return number;
     default:
       return Number::invalid;
@@ -2486,9 +2486,9 @@
 };
 
 Number WorkgroupOrStorage::Match(MatchState&, Number number) const {
-  switch (static_cast<StorageClass>(number.Value())) {
-    case StorageClass::kWorkgroup:
-    case StorageClass::kStorage:
+  switch (static_cast<AddressSpace>(number.Value())) {
+    case AddressSpace::kWorkgroup:
+    case AddressSpace::kStorage:
       return number;
     default:
       return Number::invalid;
@@ -2514,8 +2514,8 @@
 };
 
 Number Storage::Match(MatchState&, Number number) const {
-  if (number.IsAny() || number.Value() == static_cast<uint32_t>(StorageClass::kStorage)) {
-    return Number(static_cast<uint32_t>(StorageClass::kStorage));
+  if (number.IsAny() || number.Value() == static_cast<uint32_t>(AddressSpace::kStorage)) {
+    return Number(static_cast<uint32_t>(AddressSpace::kStorage));
   }
   return Number::invalid;
 }
@@ -8123,7 +8123,7 @@
   {
     /* [16] */
     /* name */ "T",
-    /* matcher index */ 51,
+    /* matcher index */ 50,
   },
   {
     /* [17] */
@@ -8133,7 +8133,7 @@
   {
     /* [18] */
     /* name */ "T",
-    /* matcher index */ 50,
+    /* matcher index */ 51,
   },
   {
     /* [19] */
@@ -8920,7 +8920,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1016],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -8932,7 +8932,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[967],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -8944,7 +8944,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[968],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -8956,7 +8956,7 @@
     /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[355],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -8968,7 +8968,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[465],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -8980,7 +8980,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[462],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -8992,7 +8992,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[459],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -9004,7 +9004,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[783],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -9016,7 +9016,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[785],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -9028,7 +9028,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[787],
     /* return matcher indices */ &kMatcherIndices[127],
@@ -9388,7 +9388,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1016],
     /* return matcher indices */ &kMatcherIndices[107],
@@ -9400,7 +9400,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[956],
     /* return matcher indices */ &kMatcherIndices[107],
@@ -9412,7 +9412,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[901],
     /* return matcher indices */ &kMatcherIndices[107],
@@ -9424,7 +9424,7 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[492],
     /* return matcher indices */ &kMatcherIndices[107],
@@ -9436,7 +9436,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[761],
     /* return matcher indices */ &kMatcherIndices[107],
@@ -9448,7 +9448,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[765],
     /* return matcher indices */ &kMatcherIndices[107],
@@ -9640,7 +9640,7 @@
     /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[1016],
     /* return matcher indices */ &kMatcherIndices[113],
@@ -9652,7 +9652,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[947],
     /* return matcher indices */ &kMatcherIndices[113],
@@ -9664,7 +9664,7 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[948],
     /* return matcher indices */ &kMatcherIndices[113],
@@ -9676,7 +9676,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[753],
     /* return matcher indices */ &kMatcherIndices[113],
@@ -11553,7 +11553,7 @@
     /* parameters */ &kParameters[456],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::select_bool,
   },
   {
     /* [277] */
@@ -11565,7 +11565,7 @@
     /* parameters */ &kParameters[549],
     /* return matcher indices */ &kMatcherIndices[33],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::select_bool,
   },
   {
     /* [278] */
@@ -11577,7 +11577,7 @@
     /* parameters */ &kParameters[552],
     /* return matcher indices */ &kMatcherIndices[33],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
+    /* const eval */ &ConstEval::select_boolvec,
   },
   {
     /* [279] */
@@ -13132,7 +13132,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[707],
     /* return matcher indices */ &kMatcherIndices[20],
@@ -13144,7 +13144,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[709],
     /* return matcher indices */ &kMatcherIndices[42],
@@ -13156,7 +13156,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[703],
     /* return matcher indices */ &kMatcherIndices[20],
@@ -13168,7 +13168,7 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[6],
     /* parameters */ &kParameters[705],
     /* return matcher indices */ &kMatcherIndices[42],
@@ -14331,9 +14331,9 @@
   },
   {
     /* [67] */
-    /* fn select<T : scalar>(T, T, bool) -> T */
-    /* fn select<T : scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
-    /* fn select<N : num, T : scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
+    /* fn select<T : abstract_or_scalar>(T, T, bool) -> T */
+    /* fn select<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
+    /* fn select<N : num, T : abstract_or_scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
     /* num overloads */ 3,
     /* overloads */ &kOverloads[276],
   },
diff --git a/src/tint/resolver/intrinsic_table.inl.tmpl b/src/tint/resolver/intrinsic_table.inl.tmpl
index 750c29f..fb95eae 100644
--- a/src/tint/resolver/intrinsic_table.inl.tmpl
+++ b/src/tint/resolver/intrinsic_table.inl.tmpl
@@ -201,7 +201,7 @@
 {{- range .TemplateParams }}
 {{-   template "DeclareLocalTemplateParam" . }}
 {{- end  }}
-  if (!match_{{TrimLeft .Name "_"}}(ty{{range .TemplateParams}}, {{.GetName}}{{end}})) {
+  if (!match_{{TrimLeft .Name "_"}}(state, ty{{range .TemplateParams}}, {{.GetName}}{{end}})) {
     return nullptr;
   }
 {{- range .TemplateParams }}
@@ -254,7 +254,7 @@
 
 const sem::Type* {{$class}}::Match(MatchState& state, const sem::Type* ty) const {
 {{- range .PrecedenceSortedTypes }}
-  if (match_{{.Name}}(ty)) {
+  if (match_{{.Name}}(state, ty)) {
     return build_{{.Name}}(state);
   }
 {{- end }}
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index b8f5629..aaf8ef9 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -53,7 +53,8 @@
 
 TEST_F(IntrinsicTableTest, MatchF32) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kCos, utils::Vector{f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kCos, utils::Vector{f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
@@ -64,7 +65,8 @@
 
 TEST_F(IntrinsicTableTest, MismatchF32) {
     auto* i32 = create<sem::I32>();
-    auto result = table->Lookup(BuiltinType::kCos, utils::Vector{i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kCos, utils::Vector{i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -73,7 +75,8 @@
     auto* f32 = create<sem::F32>();
     auto* u32 = create<sem::U32>();
     auto* vec2_f32 = create<sem::Vector>(f32, 2u);
-    auto result = table->Lookup(BuiltinType::kUnpack2x16float, utils::Vector{u32}, Source{});
+    auto result = table->Lookup(BuiltinType::kUnpack2x16float, utils::Vector{u32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kUnpack2x16float);
@@ -84,7 +87,8 @@
 
 TEST_F(IntrinsicTableTest, MismatchU32) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kUnpack2x16float, utils::Vector{f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kUnpack2x16float, utils::Vector{f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -94,7 +98,8 @@
     auto* i32 = create<sem::I32>();
     auto* vec4_f32 = create<sem::Vector>(f32, 4u);
     auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
-    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, i32, i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, i32, i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
@@ -111,14 +116,16 @@
 TEST_F(IntrinsicTableTest, MismatchI32) {
     auto* f32 = create<sem::F32>();
     auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k1d, f32);
-    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
 
 TEST_F(IntrinsicTableTest, MatchIU32AsI32) {
     auto* i32 = create<sem::I32>();
-    auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
@@ -129,7 +136,8 @@
 
 TEST_F(IntrinsicTableTest, MatchIU32AsU32) {
     auto* u32 = create<sem::U32>();
-    auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{u32}, Source{});
+    auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{u32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kCountOneBits);
@@ -140,14 +148,16 @@
 
 TEST_F(IntrinsicTableTest, MismatchIU32) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kCountOneBits, utils::Vector{f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
 
 TEST_F(IntrinsicTableTest, MatchFIU32AsI32) {
     auto* i32 = create<sem::I32>();
-    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{i32, i32, i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{i32, i32, i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
@@ -160,7 +170,8 @@
 
 TEST_F(IntrinsicTableTest, MatchFIU32AsU32) {
     auto* u32 = create<sem::U32>();
-    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{u32, u32, u32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{u32, u32, u32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
@@ -173,7 +184,8 @@
 
 TEST_F(IntrinsicTableTest, MatchFIU32AsF32) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, f32, f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, f32, f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
@@ -186,7 +198,8 @@
 
 TEST_F(IntrinsicTableTest, MismatchFIU32) {
     auto* bool_ = create<sem::Bool>();
-    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{bool_, bool_, bool_}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{bool_, bool_, bool_},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -194,7 +207,8 @@
 TEST_F(IntrinsicTableTest, MatchBool) {
     auto* f32 = create<sem::F32>();
     auto* bool_ = create<sem::Bool>();
-    auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_}, Source{});
+    auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kSelect);
@@ -207,7 +221,8 @@
 
 TEST_F(IntrinsicTableTest, MismatchBool) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -216,8 +231,9 @@
     auto* i32 = create<sem::I32>();
     auto* atomicI32 = create<sem::Atomic>(i32);
     auto* ptr =
-        create<sem::Pointer>(atomicI32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
-    auto result = table->Lookup(BuiltinType::kAtomicLoad, utils::Vector{ptr}, Source{});
+        create<sem::Pointer>(atomicI32, ast::AddressSpace::kWorkgroup, ast::Access::kReadWrite);
+    auto result = table->Lookup(BuiltinType::kAtomicLoad, utils::Vector{ptr},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kAtomicLoad);
@@ -229,15 +245,17 @@
 TEST_F(IntrinsicTableTest, MismatchPointer) {
     auto* i32 = create<sem::I32>();
     auto* atomicI32 = create<sem::Atomic>(i32);
-    auto result = table->Lookup(BuiltinType::kAtomicLoad, utils::Vector{atomicI32}, Source{});
+    auto result = table->Lookup(BuiltinType::kAtomicLoad, utils::Vector{atomicI32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
 
 TEST_F(IntrinsicTableTest, MatchArray) {
     auto* arr = create<sem::Array>(create<sem::U32>(), sem::RuntimeArrayCount{}, 4u, 4u, 4u, 4u);
-    auto* arr_ptr = create<sem::Pointer>(arr, ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{arr_ptr}, Source{});
+    auto* arr_ptr = create<sem::Pointer>(arr, ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{arr_ptr},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kArrayLength);
@@ -250,7 +268,8 @@
 
 TEST_F(IntrinsicTableTest, MismatchArray) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -261,8 +280,8 @@
     auto* vec4_f32 = create<sem::Vector>(f32, 4u);
     auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
     auto* sampler = create<sem::Sampler>(ast::SamplerKind::kSampler);
-    auto result =
-        table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, sampler, vec2_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, sampler, vec2_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureSample);
@@ -280,8 +299,8 @@
     auto* f32 = create<sem::F32>();
     auto* vec2_f32 = create<sem::Vector>(f32, 2u);
     auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
-    auto result =
-        table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, f32, vec2_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, f32, vec2_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -292,8 +311,8 @@
     auto* vec2_i32 = create<sem::Vector>(i32, 2u);
     auto* vec4_f32 = create<sem::Vector>(f32, 4u);
     auto* tex = create<sem::SampledTexture>(ast::TextureDimension::k2d, f32);
-    auto result =
-        table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
@@ -313,8 +332,8 @@
     auto* vec2_i32 = create<sem::Vector>(i32, 2u);
     auto* vec4_f32 = create<sem::Vector>(f32, 4u);
     auto* tex = create<sem::MultisampledTexture>(ast::TextureDimension::k2d, f32);
-    auto result =
-        table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
@@ -333,8 +352,8 @@
     auto* i32 = create<sem::I32>();
     auto* vec2_i32 = create<sem::Vector>(i32, 2u);
     auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
-    auto result =
-        table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
@@ -353,8 +372,8 @@
     auto* i32 = create<sem::I32>();
     auto* vec2_i32 = create<sem::Vector>(i32, 2u);
     auto* tex = create<sem::DepthMultisampledTexture>(ast::TextureDimension::k2d);
-    auto result =
-        table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32, i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
@@ -374,7 +393,8 @@
     auto* vec2_i32 = create<sem::Vector>(i32, 2u);
     auto* vec4_f32 = create<sem::Vector>(f32, 4u);
     auto* tex = create<sem::ExternalTexture>();
-    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{tex, vec2_i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureLoad);
@@ -395,8 +415,8 @@
     auto* tex = create<sem::StorageTexture>(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
                                             ast::Access::kWrite, subtype);
 
-    auto result =
-        table->Lookup(BuiltinType::kTextureStore, utils::Vector{tex, vec2_i32, vec4_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureStore, utils::Vector{tex, vec2_i32, vec4_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kTextureStore);
@@ -414,7 +434,8 @@
     auto* f32 = create<sem::F32>();
     auto* i32 = create<sem::I32>();
     auto* vec2_i32 = create<sem::Vector>(i32, 2u);
-    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{f32, vec2_i32}, Source{});
+    auto result = table->Lookup(BuiltinType::kTextureLoad, utils::Vector{f32, vec2_i32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -424,9 +445,9 @@
     auto result = table->Lookup(
         BuiltinType::kCos,
         utils::Vector{
-            create<sem::Reference>(f32, ast::StorageClass::kFunction, ast::Access::kReadWrite),
+            create<sem::Reference>(f32, ast::AddressSpace::kFunction, ast::Access::kReadWrite),
         },
-        Source{});
+        sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kCos);
@@ -437,7 +458,8 @@
 
 TEST_F(IntrinsicTableTest, MatchTemplateType) {
     auto* f32 = create<sem::F32>();
-    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, f32, f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, f32, f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
@@ -450,7 +472,8 @@
 TEST_F(IntrinsicTableTest, MismatchTemplateType) {
     auto* f32 = create<sem::F32>();
     auto* u32 = create<sem::U32>();
-    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, u32, f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{f32, u32, f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -458,8 +481,8 @@
 TEST_F(IntrinsicTableTest, MatchOpenSizeVector) {
     auto* f32 = create<sem::F32>();
     auto* vec2_f32 = create<sem::Vector>(f32, 2u);
-    auto result =
-        table->Lookup(BuiltinType::kClamp, utils::Vector{vec2_f32, vec2_f32, vec2_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{vec2_f32, vec2_f32, vec2_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kClamp);
@@ -474,8 +497,8 @@
     auto* f32 = create<sem::F32>();
     auto* u32 = create<sem::U32>();
     auto* vec2_f32 = create<sem::Vector>(f32, 2u);
-    auto result =
-        table->Lookup(BuiltinType::kClamp, utils::Vector{vec2_f32, u32, vec2_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kClamp, utils::Vector{vec2_f32, u32, vec2_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -484,7 +507,8 @@
     auto* f32 = create<sem::F32>();
     auto* vec3_f32 = create<sem::Vector>(f32, 3u);
     auto* mat3_f32 = create<sem::Matrix>(vec3_f32, 3u);
-    auto result = table->Lookup(BuiltinType::kDeterminant, utils::Vector{mat3_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kDeterminant, utils::Vector{mat3_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
     EXPECT_EQ(result.sem->Type(), BuiltinType::kDeterminant);
@@ -497,16 +521,49 @@
     auto* f32 = create<sem::F32>();
     auto* vec2_f32 = create<sem::Vector>(f32, 2u);
     auto* mat3x2_f32 = create<sem::Matrix>(vec2_f32, 3u);
-    auto result = table->Lookup(BuiltinType::kDeterminant, utils::Vector{mat3x2_f32}, Source{});
+    auto result = table->Lookup(BuiltinType::kDeterminant, utils::Vector{mat3x2_f32},
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
 
+TEST_F(IntrinsicTableTest, MatchDifferentArgsElementType_ConstantEval) {
+    auto* af = create<sem::AbstractFloat>();
+    auto* bool_ = create<sem::Bool>();
+    auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{af, af, bool_},
+                                sem::EvaluationStage::kConstant, Source{});
+    ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
+    ASSERT_EQ(Diagnostics().str(), "");
+    EXPECT_EQ(result.sem->Type(), BuiltinType::kSelect);
+    EXPECT_EQ(result.sem->ReturnType(), af);
+    ASSERT_EQ(result.sem->Parameters().Length(), 3u);
+    EXPECT_EQ(result.sem->Parameters()[0]->Type(), af);
+    EXPECT_EQ(result.sem->Parameters()[1]->Type(), af);
+    EXPECT_EQ(result.sem->Parameters()[2]->Type(), bool_);
+}
+
+TEST_F(IntrinsicTableTest, MatchDifferentArgsElementType_RuntimeEval) {
+    auto* af = create<sem::AbstractFloat>();
+    auto* bool_ref = create<sem::Reference>(create<sem::Bool>(), ast::AddressSpace::kFunction,
+                                            ast::Access::kReadWrite);
+    auto result = table->Lookup(BuiltinType::kSelect, utils::Vector{af, af, bool_ref},
+                                sem::EvaluationStage::kRuntime, Source{});
+    ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
+    ASSERT_EQ(Diagnostics().str(), "");
+    EXPECT_EQ(result.sem->Type(), BuiltinType::kSelect);
+    EXPECT_TRUE(result.sem->ReturnType()->Is<sem::F32>());
+    ASSERT_EQ(result.sem->Parameters().Length(), 3u);
+    EXPECT_TRUE(result.sem->Parameters()[0]->Type()->Is<sem::F32>());
+    EXPECT_TRUE(result.sem->Parameters()[1]->Type()->Is<sem::F32>());
+    EXPECT_TRUE(result.sem->Parameters()[2]->Type()->Is<sem::Bool>());
+}
+
 TEST_F(IntrinsicTableTest, OverloadOrderByNumberOfParameters) {
     // None of the arguments match, so expect the overloads with 2 parameters to
     // come first
     auto* bool_ = create<sem::Bool>();
-    table->Lookup(BuiltinType::kTextureDimensions, utils::Vector{bool_, bool_}, Source{});
+    table->Lookup(BuiltinType::kTextureDimensions, utils::Vector{bool_, bool_},
+                  sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(Diagnostics().str(),
               R"(error: no matching call to textureDimensions(bool, bool)
 
@@ -544,7 +601,8 @@
 TEST_F(IntrinsicTableTest, OverloadOrderByMatchingParameter) {
     auto* tex = create<sem::DepthTexture>(ast::TextureDimension::k2d);
     auto* bool_ = create<sem::Bool>();
-    table->Lookup(BuiltinType::kTextureDimensions, utils::Vector{tex, bool_}, Source{});
+    table->Lookup(BuiltinType::kTextureDimensions, utils::Vector{tex, bool_},
+                  sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(Diagnostics().str(),
               R"(error: no matching call to textureDimensions(texture_depth_2d, bool)
 
@@ -583,15 +641,17 @@
     auto* f32 = create<sem::F32>();
     auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
     auto* bool_ = create<sem::Bool>();
-    auto a = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_}, Source{});
+    auto a = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_},
+                           sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(a.sem, nullptr) << Diagnostics().str();
 
-    auto b = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_}, Source{});
+    auto b = table->Lookup(BuiltinType::kSelect, utils::Vector{f32, f32, bool_},
+                           sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(b.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
 
-    auto c =
-        table->Lookup(BuiltinType::kSelect, utils::Vector{vec2_f32, vec2_f32, bool_}, Source{});
+    auto c = table->Lookup(BuiltinType::kSelect, utils::Vector{vec2_f32, vec2_f32, bool_},
+                           sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(c.sem, nullptr) << Diagnostics().str();
     ASSERT_EQ(Diagnostics().str(), "");
 
@@ -827,7 +887,8 @@
     auto* f32 = create<sem::F32>();
     utils::Vector<const sem::Type*, 0> arg_tys;
     arg_tys.Resize(257, f32);
-    auto result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys), Source{});
+    auto result = table->Lookup(BuiltinType::kAbs, std::move(arg_tys),
+                                sem::EvaluationStage::kConstant, Source{});
     ASSERT_EQ(result.sem, nullptr);
     ASSERT_THAT(Diagnostics().str(), HasSubstr("no matching call"));
 }
@@ -1064,7 +1125,7 @@
     auto* arg_b = GetParam().arg_b(*this);
     auto* arg_c = GetParam().arg_c(*this);
     auto builtin = table->Lookup(sem::BuiltinType::kClamp, utils::Vector{arg_a, arg_b, arg_c},
-                                 Source{{12, 34}});
+                                 sem::EvaluationStage::kConstant, Source{{12, 34}});
 
     bool matched = builtin.sem != nullptr;
     bool expected_match = GetParam().expected_match;
@@ -1295,8 +1356,8 @@
     auto* arg_a = GetParam().arg_a(*this);
     auto* arg_b = GetParam().arg_b(*this);
     auto* arg_c = GetParam().arg_c(*this);
-    auto builtin =
-        table->Lookup(sem::BuiltinType::kMix, utils::Vector{arg_a, arg_b, arg_c}, Source{{12, 34}});
+    auto builtin = table->Lookup(sem::BuiltinType::kMix, utils::Vector{arg_a, arg_b, arg_c},
+                                 sem::EvaluationStage::kConstant, Source{{12, 34}});
 
     bool matched = builtin.sem != nullptr;
     bool expected_match = GetParam().expected_match;
diff --git a/src/tint/resolver/is_host_shareable_test.cc b/src/tint/resolver/is_host_shareable_test.cc
index 04f9b89..ab969fa 100644
--- a/src/tint/resolver/is_host_shareable_test.cc
+++ b/src/tint/resolver/is_host_shareable_test.cc
@@ -95,7 +95,7 @@
 }
 
 TEST_F(ResolverIsHostShareable, Pointer) {
-    auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::StorageClass::kPrivate,
+    auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::AddressSpace::kPrivate,
                                      ast::Access::kReadWrite);
     EXPECT_FALSE(r()->IsHostShareable(ptr));
 }
diff --git a/src/tint/resolver/is_storeable_test.cc b/src/tint/resolver/is_storeable_test.cc
index 3662f38..61cf33b 100644
--- a/src/tint/resolver/is_storeable_test.cc
+++ b/src/tint/resolver/is_storeable_test.cc
@@ -78,7 +78,7 @@
 }
 
 TEST_F(ResolverIsStorableTest, Pointer) {
-    auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::StorageClass::kPrivate,
+    auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::AddressSpace::kPrivate,
                                      ast::Access::kReadWrite);
     EXPECT_FALSE(r()->IsStorable(ptr));
 }
@@ -111,7 +111,7 @@
 TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
     Structure("S", utils::Vector{
                        Member("a", ty.i32()),
-                       Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+                       Member("b", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -137,7 +137,7 @@
     auto* non_storable =
         Structure("nonstorable", utils::Vector{
                                      Member("a", ty.i32()),
-                                     Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+                                     Member("b", ty.pointer<i32>(ast::AddressSpace::kPrivate)),
                                  });
     Structure("S", utils::Vector{
                        Member("a", ty.i32()),
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index dd200bc..e8a1de5 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -902,7 +902,7 @@
                 utils::Vector{WorkgroupSize(abstract_expr()), Stage(ast::PipelineStage::kCompute)});
             break;
         case Method::kIndex:
-            GlobalVar("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
+            GlobalVar("arr", ty.array<i32, 4>(), ast::AddressSpace::kPrivate);
             WrapInFunction(IndexAccessor("arr", abstract_expr()));
             break;
         case Method::kRuntimeIndex:
diff --git a/src/tint/resolver/ptr_ref_test.cc b/src/tint/resolver/ptr_ref_test.cc
index 129263a..a42d554 100644
--- a/src/tint/resolver/ptr_ref_test.cc
+++ b/src/tint/resolver/ptr_ref_test.cc
@@ -38,7 +38,7 @@
 
     ASSERT_TRUE(TypeOf(expr)->Is<sem::Pointer>());
     EXPECT_TRUE(TypeOf(expr)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
-    EXPECT_EQ(TypeOf(expr)->As<sem::Pointer>()->StorageClass(), ast::StorageClass::kFunction);
+    EXPECT_EQ(TypeOf(expr)->As<sem::Pointer>()->AddressSpace(), ast::AddressSpace::kFunction);
 }
 
 TEST_F(ResolverPtrRefTest, AddressOfThenDeref) {
@@ -56,28 +56,28 @@
     EXPECT_TRUE(TypeOf(expr)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
 }
 
-TEST_F(ResolverPtrRefTest, DefaultPtrStorageClass) {
+TEST_F(ResolverPtrRefTest, DefaultPtrAddressSpace) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
     auto* buf = Structure("S", utils::Vector{Member("m", ty.i32())});
     auto* function = Var("f", ty.i32());
-    auto* private_ = GlobalVar("p", ty.i32(), ast::StorageClass::kPrivate);
-    auto* workgroup = GlobalVar("w", ty.i32(), ast::StorageClass::kWorkgroup);
+    auto* private_ = GlobalVar("p", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* workgroup = GlobalVar("w", ty.i32(), ast::AddressSpace::kWorkgroup);
     auto* uniform =
-        GlobalVar("ub", ty.Of(buf), ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+        GlobalVar("ub", ty.Of(buf), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
     auto* storage =
-        GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage, Binding(1_a), Group(0_a));
+        GlobalVar("sb", ty.Of(buf), ast::AddressSpace::kStorage, Binding(1_a), Group(0_a));
 
     auto* function_ptr =
-        Let("f_ptr", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(function));
+        Let("f_ptr", ty.pointer(ty.i32(), ast::AddressSpace::kFunction), AddressOf(function));
     auto* private_ptr =
-        Let("p_ptr", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(private_));
+        Let("p_ptr", ty.pointer(ty.i32(), ast::AddressSpace::kPrivate), AddressOf(private_));
     auto* workgroup_ptr =
-        Let("w_ptr", ty.pointer(ty.i32(), ast::StorageClass::kWorkgroup), AddressOf(workgroup));
+        Let("w_ptr", ty.pointer(ty.i32(), ast::AddressSpace::kWorkgroup), AddressOf(workgroup));
     auto* uniform_ptr =
-        Let("ub_ptr", ty.pointer(ty.Of(buf), ast::StorageClass::kUniform), AddressOf(uniform));
+        Let("ub_ptr", ty.pointer(ty.Of(buf), ast::AddressSpace::kUniform), AddressOf(uniform));
     auto* storage_ptr =
-        Let("sb_ptr", ty.pointer(ty.Of(buf), ast::StorageClass::kStorage), AddressOf(storage));
+        Let("sb_ptr", ty.pointer(ty.Of(buf), ast::AddressSpace::kStorage), AddressOf(storage));
 
     WrapInFunction(function, function_ptr, private_ptr, workgroup_ptr, uniform_ptr, storage_ptr);
 
diff --git a/src/tint/resolver/ptr_ref_validation_test.cc b/src/tint/resolver/ptr_ref_validation_test.cc
index f850f2a..176a836 100644
--- a/src/tint/resolver/ptr_ref_validation_test.cc
+++ b/src/tint/resolver/ptr_ref_validation_test.cc
@@ -62,7 +62,7 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: cannot take the address of expression in handle "
-              "storage class");
+              "address space");
 }
 
 TEST_F(ResolverPtrRefValidationTest, AddressOfVectorComponent_MemberAccessor) {
@@ -102,7 +102,7 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: cannot take the address of expression in handle "
-              "storage class");
+              "address space");
 }
 
 TEST_F(ResolverPtrRefValidationTest, DerefOfLiteral) {
@@ -143,12 +143,12 @@
     // }
     auto* inner = Structure("Inner", utils::Vector{Member("arr", ty.array<i32, 4>())});
     auto* buf = Structure("S", utils::Vector{Member("inner", ty.Of(inner))});
-    auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    auto* storage = GlobalVar("s", ty.Of(buf), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
                               Binding(0_a), Group(0_a));
 
     auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 2_i);
     auto* ptr =
-        Let(Source{{12, 34}}, "p", ty.pointer<i32>(ast::StorageClass::kStorage), AddressOf(expr));
+        Let(Source{{12, 34}}, "p", ty.pointer<i32>(ast::AddressSpace::kStorage), AddressOf(expr));
 
     WrapInFunction(ptr);
 
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index a7e917d..f8db697 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -89,6 +89,11 @@
 #include "src/tint/utils/vector.h"
 
 namespace tint::resolver {
+namespace {
+
+constexpr int64_t kMaxArrayElementCount = 65536;
+
+}  // namespace
 
 Resolver::Resolver(ProgramBuilder* builder)
     : builder_(builder),
@@ -167,7 +172,7 @@
 
     if (!enabled_extensions_.Contains(ast::Extension::kChromiumDisableUniformityAnalysis)) {
         if (!AnalyzeUniformity(builder_, dependencies_)) {
-            // TODO(jrprice): Reject programs that fail uniformity analysis.
+            return false;
         }
     }
 
@@ -247,9 +252,9 @@
             if (auto* el = Type(t->type)) {
                 auto access = t->access;
                 if (access == ast::kUndefined) {
-                    access = DefaultAccessForStorageClass(t->storage_class);
+                    access = DefaultAccessForAddressSpace(t->address_space);
                 }
-                return builder_->create<sem::Pointer>(el, t->storage_class, access);
+                return builder_->create<sem::Pointer>(el, t->address_space, access);
             }
             return nullptr;
         },
@@ -368,11 +373,11 @@
         ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
     }
 
-    if (rhs && !validator_.VariableInitializer(v, ast::StorageClass::kNone, ty, rhs)) {
+    if (rhs && !validator_.VariableInitializer(v, ast::AddressSpace::kNone, ty, rhs)) {
         return nullptr;
     }
 
-    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+    if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, const_cast<sem::Type*>(ty),
                                       v->source)) {
         AddNote("while instantiating 'let' " + builder_->Symbols().NameFor(v->symbol), v->source);
         return nullptr;
@@ -381,12 +386,12 @@
     sem::Variable* sem = nullptr;
     if (is_global) {
         sem = builder_->create<sem::GlobalVariable>(
-            v, ty, sem::EvaluationStage::kRuntime, ast::StorageClass::kNone,
+            v, ty, sem::EvaluationStage::kRuntime, ast::AddressSpace::kNone,
             ast::Access::kUndefined, /* constant_value */ nullptr, sem::BindingPoint{},
             std::nullopt);
     } else {
         sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime,
-                                                   ast::StorageClass::kNone,
+                                                   ast::AddressSpace::kNone,
                                                    ast::Access::kUndefined, current_statement_,
                                                    /* constant_value */ nullptr);
     }
@@ -425,11 +430,11 @@
         return nullptr;
     }
 
-    if (rhs && !validator_.VariableInitializer(v, ast::StorageClass::kNone, ty, rhs)) {
+    if (rhs && !validator_.VariableInitializer(v, ast::AddressSpace::kNone, ty, rhs)) {
         return nullptr;
     }
 
-    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+    if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, const_cast<sem::Type*>(ty),
                                       v->source)) {
         AddNote("while instantiating 'override' " + builder_->Symbols().NameFor(v->symbol),
                 v->source);
@@ -437,7 +442,7 @@
     }
 
     auto* sem = builder_->create<sem::GlobalVariable>(
-        v, ty, sem::EvaluationStage::kOverride, ast::StorageClass::kNone, ast::Access::kUndefined,
+        v, ty, sem::EvaluationStage::kOverride, ast::AddressSpace::kNone, ast::Access::kUndefined,
         /* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
     sem->SetConstructor(rhs);
 
@@ -510,21 +515,21 @@
         return nullptr;
     }
 
-    if (!validator_.VariableInitializer(c, ast::StorageClass::kNone, ty, rhs)) {
+    if (!validator_.VariableInitializer(c, ast::AddressSpace::kNone, ty, rhs)) {
         return nullptr;
     }
 
-    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+    if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, const_cast<sem::Type*>(ty),
                                       c->source)) {
         AddNote("while instantiating 'const' " + builder_->Symbols().NameFor(c->symbol), c->source);
         return nullptr;
     }
 
     auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
-                                c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone,
+                                c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
                                 ast::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
                           : static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
-                                c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone,
+                                c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
                                 ast::Access::kUndefined, current_statement_, value));
 
     sem->SetConstructor(rhs);
@@ -562,39 +567,39 @@
         return nullptr;
     }
 
-    auto storage_class = var->declared_storage_class;
-    if (storage_class == ast::StorageClass::kNone) {
-        // No declared storage class. Infer from usage / type.
+    auto address_space = var->declared_address_space;
+    if (address_space == ast::AddressSpace::kNone) {
+        // No declared address space. Infer from usage / type.
         if (!is_global) {
-            storage_class = ast::StorageClass::kFunction;
+            address_space = ast::AddressSpace::kFunction;
         } else if (storage_ty->UnwrapRef()->is_handle()) {
             // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
             // If the store type is a texture type or a sampler type, then the
-            // variable declaration must not have a storage class attribute. The
-            // storage class will always be handle.
-            storage_class = ast::StorageClass::kHandle;
+            // variable declaration must not have a address space attribute. The
+            // address space will always be handle.
+            address_space = ast::AddressSpace::kHandle;
         }
     }
 
-    if (!is_global && storage_class != ast::StorageClass::kFunction &&
+    if (!is_global && address_space != ast::AddressSpace::kFunction &&
         validator_.IsValidationEnabled(var->attributes,
-                                       ast::DisabledValidation::kIgnoreStorageClass)) {
-        AddError("function-scope 'var' declaration must use 'function' storage class", var->source);
+                                       ast::DisabledValidation::kIgnoreAddressSpace)) {
+        AddError("function-scope 'var' declaration must use 'function' address space", var->source);
         return nullptr;
     }
 
     auto access = var->declared_access;
     if (access == ast::Access::kUndefined) {
-        access = DefaultAccessForStorageClass(storage_class);
+        access = DefaultAccessForAddressSpace(address_space);
     }
 
-    if (rhs && !validator_.VariableInitializer(var, storage_class, storage_ty, rhs)) {
+    if (rhs && !validator_.VariableInitializer(var, address_space, storage_ty, rhs)) {
         return nullptr;
     }
 
-    auto* var_ty = builder_->create<sem::Reference>(storage_ty, storage_class, access);
+    auto* var_ty = builder_->create<sem::Reference>(storage_ty, address_space, access);
 
-    if (!ApplyStorageClassUsageToType(storage_class, var_ty, var->source)) {
+    if (!ApplyAddressSpaceUsageToType(address_space, var_ty, var->source)) {
         AddNote("while instantiating 'var' " + builder_->Symbols().NameFor(var->symbol),
                 var->source);
         return nullptr;
@@ -654,12 +659,12 @@
         }
 
         sem = builder_->create<sem::GlobalVariable>(
-            var, var_ty, sem::EvaluationStage::kRuntime, storage_class, access,
+            var, var_ty, sem::EvaluationStage::kRuntime, address_space, access,
             /* constant_value */ nullptr, binding_point, location);
 
     } else {
         sem = builder_->create<sem::LocalVariable>(var, var_ty, sem::EvaluationStage::kRuntime,
-                                                   storage_class, access, current_statement_,
+                                                   address_space, access, current_statement_,
                                                    /* constant_value */ nullptr);
     }
 
@@ -686,7 +691,7 @@
         return nullptr;
     }
 
-    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, ty, param->source)) {
+    if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, ty, param->source)) {
         add_note();
         return nullptr;
     }
@@ -694,8 +699,8 @@
     if (auto* ptr = ty->As<sem::Pointer>()) {
         // For MSL, we push module-scope variables into the entry point as pointer
         // parameters, so we also need to handle their store type.
-        if (!ApplyStorageClassUsageToType(
-                ptr->StorageClass(), const_cast<sem::Type*>(ptr->StoreType()), param->source)) {
+        if (!ApplyAddressSpaceUsageToType(
+                ptr->AddressSpace(), const_cast<sem::Type*>(ptr->StoreType()), param->source)) {
             add_note();
             return nullptr;
         }
@@ -749,18 +754,18 @@
     }
 
     auto* sem = builder_->create<sem::Parameter>(
-        param, index, ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+        param, index, ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
         sem::ParameterUsage::kNone, binding_point, location);
     builder_->Sem().Add(param, sem);
     return sem;
 }
 
-ast::Access Resolver::DefaultAccessForStorageClass(ast::StorageClass storage_class) {
+ast::Access Resolver::DefaultAccessForAddressSpace(ast::AddressSpace address_space) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
-    switch (storage_class) {
-        case ast::StorageClass::kStorage:
-        case ast::StorageClass::kUniform:
-        case ast::StorageClass::kHandle:
+    switch (address_space) {
+        case ast::AddressSpace::kStorage:
+        case ast::AddressSpace::kUniform:
+        case ast::AddressSpace::kHandle:
             return ast::Access::kRead;
         default:
             break;
@@ -845,7 +850,7 @@
 
     // TODO(bclayton): Call this at the end of resolve on all uniform and storage
     // referenced structs
-    if (!validator_.StorageClassLayout(sem, enabled_extensions_, valid_type_storage_layouts_)) {
+    if (!validator_.AddressSpaceLayout(sem, enabled_extensions_, valid_type_storage_layouts_)) {
         return nullptr;
     }
 
@@ -962,7 +967,7 @@
     }
 
     if (auto* str = return_type->As<sem::Struct>()) {
-        if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, str, decl->source)) {
+        if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, str, decl->source)) {
             AddNote(
                 "while instantiating return type for " + builder_->Symbols().NameFor(decl->symbol),
                 decl->source);
@@ -1625,7 +1630,7 @@
 
     // If we're extracting from a reference, we return a reference.
     if (auto* ref = obj_raw_ty->As<sem::Reference>()) {
-        ty = builder_->create<sem::Reference>(ty, ref->StorageClass(), ref->Access());
+        ty = builder_->create<sem::Reference>(ty, ref->AddressSpace(), ref->Access());
     }
 
     auto stage = sem::EarliestStage(obj->Stage(), idx->Stage());
@@ -1783,7 +1788,7 @@
                                 nullptr,                   // declaration
                                 static_cast<uint32_t>(i),  // index
                                 arr->ElemType(),           // type
-                                ast::StorageClass::kNone,  // storage_class
+                                ast::AddressSpace::kNone,  // address_space
                                 ast::Access::kUndefined);
                         });
                         return builder_->create<sem::TypeConstructor>(arr, std::move(params),
@@ -1812,7 +1817,7 @@
                                 nullptr,                    // declaration
                                 static_cast<uint32_t>(i),   // index
                                 str->Members()[i]->Type(),  // type
-                                ast::StorageClass::kNone,   // storage_class
+                                ast::AddressSpace::kNone,   // address_space
                                 ast::Access::kUndefined);   // access
                         }
                         return builder_->create<sem::TypeConstructor>(str, std::move(params),
@@ -1994,10 +1999,15 @@
 sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
                                  sem::BuiltinType builtin_type,
                                  utils::Vector<const sem::Expression*, N>& args) {
+    auto arg_stage = sem::EvaluationStage::kConstant;
+    for (auto* arg : args) {
+        arg_stage = sem::EarliestStage(arg_stage, arg->Stage());
+    }
+
     IntrinsicTable::Builtin builtin;
     {
         auto arg_tys = utils::Transform(args, [](auto* arg) { return arg->Type(); });
-        builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, expr->source);
+        builtin = intrinsic_table_->Lookup(builtin_type, arg_tys, arg_stage, expr->source);
         if (!builtin.sem) {
             return nullptr;
         }
@@ -2011,19 +2021,8 @@
         AddWarning("use of deprecated builtin", expr->source);
     }
 
-    auto stage = builtin.sem->Stage();
-    if (stage == sem::EvaluationStage::kConstant) {  // <-- Optimization
-        // If the builtin is not annotated with @const, then it can only be evaluated
-        // at runtime, in which case there's no point checking the evaluation stage of the
-        // arguments.
-
-        // The builtin is @const annotated. Check all arguments are also constant.
-        for (auto* arg : args) {
-            stage = sem::EarliestStage(stage, arg->Stage());
-        }
-    }
-
     // If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
+    auto stage = sem::EarliestStage(arg_stage, builtin.sem->Stage());
     const sem::Constant* value = nullptr;
     if (stage == sem::EvaluationStage::kConstant) {
         auto const_args = ConvertArguments(args, builtin.sem);
@@ -2321,7 +2320,7 @@
 
             // If we're extracting from a reference, we return a reference.
             if (auto* ref = structure->As<sem::Reference>()) {
-                ty = builder_->create<sem::Reference>(ty, ref->StorageClass(), ref->Access());
+                ty = builder_->create<sem::Reference>(ty, ref->AddressSpace(), ref->Access());
             }
 
             auto val = const_eval_.MemberAccess(object, member);
@@ -2390,7 +2389,7 @@
                 ty = vec->type();
                 // If we're extracting from a reference, we return a reference.
                 if (auto* ref = structure->As<sem::Reference>()) {
-                    ty = builder_->create<sem::Reference>(ty, ref->StorageClass(), ref->Access());
+                    ty = builder_->create<sem::Reference>(ty, ref->AddressSpace(), ref->Access());
                 }
             } else {
                 // The vector will have a number of components equal to the length of
@@ -2483,7 +2482,7 @@
         case ast::UnaryOp::kAddressOf:
             if (auto* ref = expr_ty->As<sem::Reference>()) {
                 if (ref->StoreType()->UnwrapRef()->is_handle()) {
-                    AddError("cannot take the address of expression in handle storage class",
+                    AddError("cannot take the address of expression in handle address space",
                              unary->expr->source);
                     return nullptr;
                 }
@@ -2496,7 +2495,7 @@
                     return nullptr;
                 }
 
-                ty = builder_->create<sem::Pointer>(ref->StoreType(), ref->StorageClass(),
+                ty = builder_->create<sem::Pointer>(ref->StoreType(), ref->AddressSpace(),
                                                     ref->Access());
 
                 source_var = expr->SourceVariable();
@@ -2508,7 +2507,7 @@
 
         case ast::UnaryOp::kIndirection:
             if (auto* ptr = expr_ty->As<sem::Pointer>()) {
-                ty = builder_->create<sem::Reference>(ptr->StoreType(), ptr->StorageClass(),
+                ty = builder_->create<sem::Reference>(ptr->StoreType(), ptr->AddressSpace(),
                                                       ptr->Access());
                 source_var = expr->SourceVariable();
             } else {
@@ -2596,7 +2595,7 @@
 
     sem::ArrayCount el_count = sem::RuntimeArrayCount{};
 
-    // Evaluate the constant array size expression.
+    // Evaluate the constant array count expression.
     if (auto* count_expr = arr->count) {
         if (auto count = ArrayCount(count_expr)) {
             el_count = count.Get();
@@ -2625,7 +2624,7 @@
 }
 
 utils::Result<sem::ArrayCount> Resolver::ArrayCount(const ast::Expression* count_expr) {
-    // Evaluate the constant array size expression.
+    // Evaluate the constant array count expression.
     const auto* count_sem = Materialize(Expression(count_expr));
     if (!count_sem) {
         return utils::Failure;
@@ -2643,13 +2642,13 @@
 
     auto* count_val = count_sem->ConstantValue();
     if (!count_val) {
-        AddError("array size must evaluate to a constant integer expression or override variable",
+        AddError("array count must evaluate to a constant integer expression or override variable",
                  count_expr->source);
         return utils::Failure;
     }
 
     if (auto* ty = count_val->Type(); !ty->is_integer_scalar()) {
-        AddError("array size must evaluate to a constant integer expression, but is type '" +
+        AddError("array count must evaluate to a constant integer expression, but is type '" +
                      builder_->FriendlyName(ty) + "'",
                  count_expr->source);
         return utils::Failure;
@@ -2657,7 +2656,7 @@
 
     int64_t count = count_val->As<AInt>();
     if (count < 1) {
-        AddError("array size (" + std::to_string(count) + ") must be greater than 0",
+        AddError("array count (" + std::to_string(count) + ") must be greater than 0",
                  count_expr->source);
         return utils::Failure;
     }
@@ -2704,7 +2703,8 @@
         size = const_count->value * stride;
         if (size > std::numeric_limits<uint32_t>::max()) {
             std::stringstream msg;
-            msg << "array size (0x" << std::hex << size << ") must not exceed 0xffffffff bytes";
+            msg << "array byte size (0x" << std::hex << size
+                << ") must not exceed 0xffffffff bytes";
             AddError(msg.str(), count_source);
             return nullptr;
         }
@@ -2821,15 +2821,20 @@
                 if (!materialized) {
                     return nullptr;
                 }
+                if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
+                    AddError("'align' must be an i32 or u32 value", a->source);
+                    return nullptr;
+                }
+
                 auto const_value = materialized->ConstantValue();
                 if (!const_value) {
-                    AddError("'align' must be constant expression", a->expr->source);
+                    AddError("'align' must be constant expression", a->source);
                     return nullptr;
                 }
                 auto value = const_value->As<AInt>();
 
                 if (value <= 0 || !utils::IsPowerOfTwo(value)) {
-                    AddError("align value must be a positive, power-of-two integer", a->source);
+                    AddError("'align' value must be a positive, power-of-two integer", a->source);
                     return nullptr;
                 }
                 align = const_value->As<u32>();
@@ -3200,20 +3205,21 @@
     });
 }
 
-bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
+bool Resolver::ApplyAddressSpaceUsageToType(ast::AddressSpace address_space,
                                             sem::Type* ty,
                                             const Source& usage) {
     ty = const_cast<sem::Type*>(ty->UnwrapRef());
 
     if (auto* str = ty->As<sem::Struct>()) {
-        if (str->StorageClassUsage().count(sc)) {
+        if (str->AddressSpaceUsage().count(address_space)) {
             return true;  // Already applied
         }
 
-        str->AddUsage(sc);
+        str->AddUsage(address_space);
 
         for (auto* member : str->Members()) {
-            if (!ApplyStorageClassUsageToType(sc, const_cast<sem::Type*>(member->Type()), usage)) {
+            if (!ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(member->Type()),
+                                              usage)) {
                 std::stringstream err;
                 err << "while analysing structure member " << sem_.TypeNameOf(str) << "."
                     << builder_->Symbols().NameFor(member->Declaration()->symbol);
@@ -3225,21 +3231,29 @@
     }
 
     if (auto* arr = ty->As<sem::Array>()) {
-        if (arr->IsRuntimeSized() && sc != ast::StorageClass::kStorage) {
-            AddError(
-                "runtime-sized arrays can only be used in the <storage> storage "
-                "class",
-                usage);
-            return false;
-        }
+        if (address_space != ast::AddressSpace::kStorage) {
+            if (arr->IsRuntimeSized()) {
+                AddError("runtime-sized arrays can only be used in the <storage> address space",
+                         usage);
+                return false;
+            }
 
-        return ApplyStorageClassUsageToType(sc, const_cast<sem::Type*>(arr->ElemType()), usage);
+            auto count = arr->ConstantCount();
+            if (count.has_value() && count.value() >= kMaxArrayElementCount) {
+                AddError("array count (" + std::to_string(count.value()) + ") must be less than " +
+                             std::to_string(kMaxArrayElementCount),
+                         usage);
+                return false;
+            }
+        }
+        return ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(arr->ElemType()),
+                                            usage);
     }
 
-    if (ast::IsHostShareable(sc) && !validator_.IsHostShareable(ty)) {
+    if (ast::IsHostShareable(address_space) && !validator_.IsHostShareable(ty)) {
         std::stringstream err;
-        err << "Type '" << sem_.TypeNameOf(ty) << "' cannot be used in storage class '" << sc
-            << "' as it is non-host-shareable";
+        err << "Type '" << sem_.TypeNameOf(ty) << "' cannot be used in address space '"
+            << address_space << "' as it is non-host-shareable";
         AddError(err.str(), usage);
         return false;
     }
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 7e115b3..657b313 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -113,45 +113,6 @@
   private:
     Validator::ValidTypeStorageLayouts valid_type_storage_layouts_;
 
-    /// Structure holding semantic information about a block (i.e. scope), such as
-    /// parent block and variables declared in the block.
-    /// Used to validate variable scoping rules.
-    struct BlockInfo {
-        enum class Type { kGeneric, kLoop, kLoopContinuing, kSwitchCase };
-
-        BlockInfo(const ast::BlockStatement* block, Type type, BlockInfo* parent);
-        ~BlockInfo();
-
-        template <typename Pred>
-        BlockInfo* FindFirstParent(Pred&& pred) {
-            BlockInfo* curr = this;
-            while (curr && !pred(curr)) {
-                curr = curr->parent;
-            }
-            return curr;
-        }
-
-        BlockInfo* FindFirstParent(BlockInfo::Type ty) {
-            return FindFirstParent([ty](auto* block_info) { return block_info->type == ty; });
-        }
-
-        ast::BlockStatement const* const block;
-        const Type type;
-        BlockInfo* const parent;
-        std::vector<const ast::Variable*> decls;
-
-        // first_continue is set to the index of the first variable in decls
-        // declared after the first continue statement in a loop block, if any.
-        constexpr static size_t kNoContinue = size_t(~0);
-        size_t first_continue = kNoContinue;
-    };
-
-    // Structure holding information for a TypeDecl
-    struct TypeDeclInfo {
-        ast::TypeDecl const* const ast;
-        sem::Type* const sem;
-    };
-
     /// Resolves the program, without creating final the semantic nodes.
     /// @returns true on success, false on error
     bool ResolveInternal();
@@ -387,20 +348,20 @@
     /// @param index the index of the parameter
     sem::Parameter* Parameter(const ast::Parameter* param, uint32_t index);
 
-    /// Records the storage class usage for the given type, and any transient
+    /// Records the address space usage for the given type, and any transient
     /// dependencies of the type. Validates that the type can be used for the
-    /// given storage class, erroring if it cannot.
-    /// @param sc the storage class to apply to the type and transitent types
-    /// @param ty the type to apply the storage class on
+    /// given address space, erroring if it cannot.
+    /// @param sc the address space to apply to the type and transitent types
+    /// @param ty the type to apply the address space on
     /// @param usage the Source of the root variable declaration that uses the
-    /// given type and storage class. Used for generating sensible error
+    /// given type and address space. Used for generating sensible error
     /// messages.
     /// @returns true on success, false on error
-    bool ApplyStorageClassUsageToType(ast::StorageClass sc, sem::Type* ty, const Source& usage);
+    bool ApplyAddressSpaceUsageToType(ast::AddressSpace sc, sem::Type* ty, const Source& usage);
 
-    /// @param storage_class the storage class
-    /// @returns the default access control for the given storage class
-    ast::Access DefaultAccessForStorageClass(ast::StorageClass storage_class);
+    /// @param address_space the address space
+    /// @returns the default access control for the given address space
+    ast::Access DefaultAccessForAddressSpace(ast::AddressSpace address_space);
 
     /// Allocate constant IDs for pipeline-overridable constants.
     /// @returns true on success, false on error
diff --git a/src/tint/resolver/resolver_is_storeable_test.cc b/src/tint/resolver/resolver_is_storeable_test.cc
deleted file mode 100644
index f4cde70..0000000
--- a/src/tint/resolver/resolver_is_storeable_test.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/resolver/resolver.h"
-
-#include "gmock/gmock.h"
-#include "src/tint/resolver/resolver_test_helper.h"
-#include "src/tint/sem/atomic.h"
-
-namespace tint::resolver {
-namespace {
-
-using ResolverIsStorableTest = ResolverTest;
-
-TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
-    Structure("S", {
-                       Member("a", ty.i32()),
-                       Member("b", ty.f32()),
-                   });
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
-    Structure("S", {
-                       Member("a", ty.i32()),
-                       Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
-                   });
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
-}
-
-TEST_F(ResolverIsStorableTest, Struct_NestedStorable) {
-    auto* storable = Structure("Storable", {
-                                               Member("a", ty.i32()),
-                                               Member("b", ty.f32()),
-                                           });
-    Structure("S", {
-                       Member("a", ty.i32()),
-                       Member("b", ty.Of(storable)),
-                   });
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverIsStorableTest, Struct_NestedNonStorable) {
-    auto* non_storable =
-        Structure("nonstorable", {
-                                     Member("a", ty.i32()),
-                                     Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
-                                 });
-    Structure("S", {
-                       Member("a", ty.i32()),
-                       Member("b", ty.Of(non_storable)),
-                   });
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
-}
-
-}  // namespace
-}  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index cde4735..631725a 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -316,7 +316,7 @@
 
 TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
     auto* init = Expr(2_i);
-    GlobalVar("my_var", ty.i32(), ast::StorageClass::kPrivate, init);
+    GlobalVar("my_var", ty.i32(), ast::AddressSpace::kPrivate, init);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -396,7 +396,7 @@
     Func("func_i32", utils::Empty, ty.void_(), utils::Vector{fn_i32_decl});
 
     // Declare f32 "foo" at module scope
-    auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kPrivate, Expr(2_f));
+    auto* mod_f32 = Var("foo", ty.f32(), ast::AddressSpace::kPrivate, Expr(2_f));
     auto* mod_init = mod_f32->constructor;
     AST().AddGlobalVariable(mod_f32);
 
@@ -424,7 +424,7 @@
 
 TEST_F(ResolverTest, ArraySize_UnsignedLiteral) {
     // var<private> a : array<f32, 10u>;
-    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr(10_u)), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr(10_u)), ast::AddressSpace::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -437,7 +437,7 @@
 
 TEST_F(ResolverTest, ArraySize_SignedLiteral) {
     // var<private> a : array<f32, 10i>;
-    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr(10_i)), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr(10_i)), ast::AddressSpace::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -452,7 +452,7 @@
     // const size = 10u;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(10_u));
-    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::AddressSpace::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -467,7 +467,7 @@
     // const size = 0;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(10_i));
-    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::AddressSpace::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -482,7 +482,7 @@
     // override size = 0;
     // var<workgroup> a : array<f32, size>;
     auto* override = Override("size", Expr(10_i));
-    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kWorkgroup);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::AddressSpace::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -500,8 +500,8 @@
     // var<workgroup> a : array<f32, size>;
     // var<workgroup> b : array<f32, size>;
     auto* override = Override("size", Expr(10_i));
-    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kWorkgroup);
-    auto* b = GlobalVar("b", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kWorkgroup);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::AddressSpace::kWorkgroup);
+    auto* b = GlobalVar("b", ty.array(ty.f32(), Expr("size")), ast::AddressSpace::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -523,7 +523,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Bitcast) {
-    GlobalVar("name", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("name", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* bitcast = create<ast::BitcastExpression>(ty.f32(), Expr("name"));
     WrapInFunction(bitcast);
@@ -586,7 +586,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Cast) {
-    GlobalVar("name", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("name", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* cast = Construct(ty.f32(), "name");
     WrapInFunction(cast);
@@ -644,7 +644,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
-    auto* my_var = GlobalVar("my_var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* my_var = GlobalVar("my_var", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* ident = Expr("my_var");
     WrapInFunction(ident);
@@ -747,7 +747,7 @@
     auto* v = Expr("v");
     auto* p = Expr("p");
     auto* v_decl = Decl(Var("v", ty.f32()));
-    auto* p_decl = Decl(Let("p", ty.pointer<f32>(ast::StorageClass::kFunction), AddressOf(v)));
+    auto* p_decl = Decl(Let("p", ty.pointer<f32>(ast::AddressSpace::kFunction), AddressOf(v)));
     auto* assign = Assign(Deref(p), 1.23_f);
     Func("my_func", utils::Empty, ty.void_(),
          utils::Vector{
@@ -822,7 +822,7 @@
     auto* param_b = Param("b", ty.u32(), utils::Vector{Builtin(ast::BuiltinValue::kVertexIndex)});
     auto* param_c = Param("c", ty.u32(), utils::Vector{Location(1_a)});
 
-    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* func = Func("my_func",
                       utils::Vector{
                           param_a,
@@ -852,8 +852,8 @@
 
 TEST_F(ResolverTest, Function_GlobalVariable_Location) {
     auto* var = GlobalVar(
-        "my_vec", ty.vec4<f32>(), ast::StorageClass::kIn,
-        utils::Vector{Location(3_a), Disable(ast::DisabledValidation::kIgnoreStorageClass)});
+        "my_vec", ty.vec4<f32>(), ast::AddressSpace::kIn,
+        utils::Vector{Location(3_a), Disable(ast::DisabledValidation::kIgnoreAddressSpace)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -865,10 +865,10 @@
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
     auto* s = Structure("S", utils::Vector{Member("m", ty.u32())});
 
-    auto* sb_var = GlobalVar("sb_var", ty.Of(s), ast::StorageClass::kStorage,
+    auto* sb_var = GlobalVar("sb_var", ty.Of(s), ast::AddressSpace::kStorage,
                              ast::Access::kReadWrite, Binding(0_a), Group(0_a));
-    auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
-    auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::AddressSpace::kWorkgroup);
+    auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("my_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -911,7 +911,7 @@
 }
 
 TEST_F(ResolverTest, Function_ReturnType_NoLocation) {
-    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* func = Func("my_func", utils::Empty, ty.vec4<f32>(),
                       utils::Vector{
                           Return("my_vec"),
@@ -933,10 +933,10 @@
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
     auto* s = Structure("S", utils::Vector{Member("m", ty.u32())});
 
-    auto* sb_var = GlobalVar("sb_var", ty.Of(s), ast::StorageClass::kStorage,
+    auto* sb_var = GlobalVar("sb_var", ty.Of(s), ast::AddressSpace::kStorage,
                              ast::Access::kReadWrite, Binding(0_a), Group(0_a));
-    auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
-    auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::AddressSpace::kWorkgroup);
+    auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::AddressSpace::kPrivate);
 
     Func("my_func", utils::Empty, ty.f32(),
          utils::Vector{Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"),
@@ -1187,7 +1187,7 @@
 TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
     auto* st = Structure(
         "S", utils::Vector{Member("first_member", ty.i32()), Member("second_member", ty.f32())});
-    GlobalVar("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
+    GlobalVar("my_struct", ty.Of(st), ast::AddressSpace::kPrivate);
 
     auto* mem = MemberAccessor("my_struct", "second_member");
     WrapInFunction(mem);
@@ -1211,7 +1211,7 @@
     auto* st = Structure(
         "S", utils::Vector{Member("first_member", ty.i32()), Member("second_member", ty.f32())});
     auto* alias = Alias("alias", ty.Of(st));
-    GlobalVar("my_struct", ty.Of(alias), ast::StorageClass::kPrivate);
+    GlobalVar("my_struct", ty.Of(alias), ast::AddressSpace::kPrivate);
 
     auto* mem = MemberAccessor("my_struct", "second_member");
     WrapInFunction(mem);
@@ -1231,7 +1231,7 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle) {
-    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
 
     auto* mem = MemberAccessor("my_vec", "xzyw");
     WrapInFunction(mem);
@@ -1249,7 +1249,7 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
-    GlobalVar("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* mem = MemberAccessor("my_vec", "b");
     WrapInFunction(mem);
@@ -1285,7 +1285,7 @@
 
     auto* stB = Structure("B", utils::Vector{Member("foo", ty.vec4<f32>())});
     auto* stA = Structure("A", utils::Vector{Member("mem", ty.array(ty.Of(stB), 3_i))});
-    GlobalVar("c", ty.Of(stA), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.Of(stA), ast::AddressSpace::kPrivate);
 
     auto* mem =
         MemberAccessor(MemberAccessor(IndexAccessor(MemberAccessor("c", "mem"), 0_i), "foo"), "yx");
@@ -1303,7 +1303,7 @@
 TEST_F(ResolverTest, Expr_MemberAccessor_InBinaryOp) {
     auto* st = Structure(
         "S", utils::Vector{Member("first_member", ty.f32()), Member("second_member", ty.f32())});
-    GlobalVar("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
+    GlobalVar("my_struct", ty.Of(st), ast::AddressSpace::kPrivate);
 
     auto* expr = Add(MemberAccessor("my_struct", "first_member"),
                      MemberAccessor("my_struct", "second_member"));
@@ -1606,8 +1606,8 @@
     ss << FriendlyName(lhs_type) << " " << params.op << " " << FriendlyName(rhs_type);
     SCOPED_TRACE(ss.str());
 
-    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
-    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1641,8 +1641,8 @@
        << FriendlyName(rhs_type);
     SCOPED_TRACE(ss.str());
 
-    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
-    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1687,8 +1687,8 @@
     ss << FriendlyName(lhs_type) << " " << op << " " << FriendlyName(rhs_type);
     SCOPED_TRACE(ss.str());
 
-    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
-    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, op, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1727,8 +1727,8 @@
         is_valid_expr = vec_size == mat_cols;
     }
 
-    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
-    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::AddressSpace::kPrivate);
 
     auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1764,8 +1764,8 @@
     auto* col = create<sem::Vector>(f32, lhs_mat_rows);
     auto* result_type = create<sem::Matrix>(col, rhs_mat_cols);
 
-    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
-    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::AddressSpace::kPrivate);
 
     auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1793,11 +1793,11 @@
     auto op = GetParam();
 
     if (op == ast::UnaryOp::kNot) {
-        GlobalVar("ident", ty.vec4<bool>(), ast::StorageClass::kPrivate);
+        GlobalVar("ident", ty.vec4<bool>(), ast::AddressSpace::kPrivate);
     } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
-        GlobalVar("ident", ty.vec4<i32>(), ast::StorageClass::kPrivate);
+        GlobalVar("ident", ty.vec4<i32>(), ast::AddressSpace::kPrivate);
     } else {
-        GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+        GlobalVar("ident", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     }
     auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
     WrapInFunction(der);
@@ -1821,7 +1821,7 @@
                                          ast::UnaryOp::kNegation,
                                          ast::UnaryOp::kNot));
 
-TEST_F(ResolverTest, StorageClass_SetsIfMissing) {
+TEST_F(ResolverTest, AddressSpace_SetsIfMissing) {
     auto* var = Var("var", ty.i32());
 
     auto* stmt = Decl(var);
@@ -1829,42 +1829,42 @@
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kFunction);
+    EXPECT_EQ(Sem().Get(var)->AddressSpace(), ast::AddressSpace::kFunction);
 }
 
-TEST_F(ResolverTest, StorageClass_SetForSampler) {
+TEST_F(ResolverTest, AddressSpace_SetForSampler) {
     auto* t = ty.sampler(ast::SamplerKind::kSampler);
     auto* var = GlobalVar("var", t, Binding(0_a), Group(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kHandle);
+    EXPECT_EQ(Sem().Get(var)->AddressSpace(), ast::AddressSpace::kHandle);
 }
 
-TEST_F(ResolverTest, StorageClass_SetForTexture) {
+TEST_F(ResolverTest, AddressSpace_SetForTexture) {
     auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
     auto* var = GlobalVar("var", t, Binding(0_a), Group(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kHandle);
+    EXPECT_EQ(Sem().Get(var)->AddressSpace(), ast::AddressSpace::kHandle);
 }
 
-TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
+TEST_F(ResolverTest, AddressSpace_DoesNotSetOnConst) {
     auto* var = Let("var", ty.i32(), Construct(ty.i32()));
     auto* stmt = Decl(var);
     Func("func", utils::Empty, ty.void_(), utils::Vector{stmt});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
-    EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kNone);
+    EXPECT_EQ(Sem().Get(var)->AddressSpace(), ast::AddressSpace::kNone);
 }
 
 TEST_F(ResolverTest, Access_SetForStorageBuffer) {
     // struct S { x : i32 };
     // var<storage> g : S;
     auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
-    auto* var = GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
+    auto* var = GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage,
                           Binding(0_a), Group(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1897,11 +1897,11 @@
     // ep_1 -> {}
     // ep_2 -> {}
 
-    GlobalVar("first", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("second", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("call_a", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("call_b", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("call_c", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("first", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("second", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("call_a", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("call_b", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("call_c", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* func_b = Func("b", utils::Empty, ty.f32(),
                         utils::Vector{
@@ -2041,8 +2041,8 @@
         {
             ProgramBuilder b;
             auto* expr = b.Expr(1_i);
-            b.GlobalVar("a", b.ty.i32(), ast::StorageClass::kPrivate, expr);
-            b.GlobalVar("b", b.ty.i32(), ast::StorageClass::kPrivate, expr);
+            b.GlobalVar("a", b.ty.i32(), ast::AddressSpace::kPrivate, expr);
+            b.GlobalVar("b", b.ty.i32(), ast::AddressSpace::kPrivate, expr);
             Resolver(&b).Resolve();
         },
         "internal compiler error: AST node 'tint::ast::IntLiteralExpression' was encountered twice "
@@ -2050,7 +2050,7 @@
 }
 
 TEST_F(ResolverTest, UnaryOp_Not) {
-    GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr(Source{{12, 34}}, "ident"));
     WrapInFunction(der);
 
@@ -2059,7 +2059,7 @@
 }
 
 TEST_F(ResolverTest, UnaryOp_Complement) {
-    GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* der =
         create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr(Source{{12, 34}}, "ident"));
     WrapInFunction(der);
@@ -2069,7 +2069,7 @@
 }
 
 TEST_F(ResolverTest, UnaryOp_Negation) {
-    GlobalVar("ident", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.u32(), ast::AddressSpace::kPrivate);
     auto* der =
         create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr(Source{{12, 34}}, "ident"));
     WrapInFunction(der);
@@ -2214,15 +2214,15 @@
 
 TEST_F(ResolverTest, ModuleDependencyOrderedDeclarations) {
     auto* f0 = Func("f0", utils::Empty, ty.void_(), utils::Empty);
-    auto* v0 = GlobalVar("v0", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v0 = GlobalVar("v0", ty.i32(), ast::AddressSpace::kPrivate);
     auto* a0 = Alias("a0", ty.i32());
     auto* s0 = Structure("s0", utils::Vector{Member("m", ty.i32())});
     auto* f1 = Func("f1", utils::Empty, ty.void_(), utils::Empty);
-    auto* v1 = GlobalVar("v1", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v1 = GlobalVar("v1", ty.i32(), ast::AddressSpace::kPrivate);
     auto* a1 = Alias("a1", ty.i32());
     auto* s1 = Structure("s1", utils::Vector{Member("m", ty.i32())});
     auto* f2 = Func("f2", utils::Empty, ty.void_(), utils::Empty);
-    auto* v2 = GlobalVar("v2", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v2 = GlobalVar("v2", ty.i32(), ast::AddressSpace::kPrivate);
     auto* a2 = Alias("a2", ty.i32());
     auto* s2 = Structure("s2", utils::Vector{Member("m", ty.i32())});
 
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index b22e14b..501cd0f 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -604,13 +604,13 @@
     /// @param b the ProgramBuilder
     /// @return a new AST alias type
     static inline const ast::Type* AST(ProgramBuilder& b) {
-        return b.create<ast::Pointer>(DataType<T>::AST(b), ast::StorageClass::kPrivate,
+        return b.create<ast::Pointer>(DataType<T>::AST(b), ast::AddressSpace::kPrivate,
                                       ast::Access::kReadWrite);
     }
     /// @param b the ProgramBuilder
     /// @return the semantic aliased type
     static inline const sem::Type* Sem(ProgramBuilder& b) {
-        return b.create<sem::Pointer>(DataType<T>::Sem(b), ast::StorageClass::kPrivate,
+        return b.create<sem::Pointer>(DataType<T>::Sem(b), ast::AddressSpace::kPrivate,
                                       ast::Access::kReadWrite);
     }
 
@@ -618,7 +618,7 @@
     /// @return a new AST expression of the pointer type
     static inline const ast::Expression* Expr(ProgramBuilder& b, ScalarArgs /*unused*/) {
         auto sym = b.Symbols().New("global_for_ptr");
-        b.GlobalVar(sym, DataType<T>::AST(b), ast::StorageClass::kPrivate);
+        b.GlobalVar(sym, DataType<T>::AST(b), ast::AddressSpace::kPrivate);
         return b.AddressOf(sym);
     }
 
diff --git a/src/tint/resolver/side_effects_test.cc b/src/tint/resolver/side_effects_test.cc
index 17604a9..8c3d387 100644
--- a/src/tint/resolver/side_effects_test.cc
+++ b/src/tint/resolver/side_effects_test.cc
@@ -30,7 +30,7 @@
     template <typename T>
     void MakeSideEffectFunc(const char* name) {
         auto global = Sym();
-        GlobalVar(global, ty.Of<T>(), ast::StorageClass::kPrivate);
+        GlobalVar(global, ty.Of<T>(), ast::AddressSpace::kPrivate);
         auto local = Sym();
         Func(name, utils::Empty, ty.Of<T>(),
              utils::Vector{
@@ -43,7 +43,7 @@
     template <typename MAKE_TYPE_FUNC>
     void MakeSideEffectFunc(const char* name, MAKE_TYPE_FUNC make_type) {
         auto global = Sym();
-        GlobalVar(global, make_type(), ast::StorageClass::kPrivate);
+        GlobalVar(global, make_type(), ast::AddressSpace::kPrivate);
         auto local = Sym();
         Func(name, utils::Empty, make_type(),
              utils::Vector{
@@ -88,7 +88,7 @@
 }
 
 TEST_F(SideEffectsTest, Call_Builtin_NoSE) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
     auto* expr = Call("dpdx", "a");
     Func("f", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)},
          utils::Vector{create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
@@ -114,7 +114,7 @@
 }
 
 TEST_F(SideEffectsTest, Call_Builtin_SE) {
-    GlobalVar("a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic(ty.i32()), ast::AddressSpace::kWorkgroup);
     auto* expr = Call("atomicAdd", AddressOf("a"), 1_i);
     WrapInFunction(expr);
 
@@ -163,20 +163,20 @@
     auto& c = GetParam();
 
     uint32_t next_binding = 0;
-    GlobalVar("f", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("i", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("u", ty.u32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("vf", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("vf2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("vi2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    GlobalVar("vf4", ty.vec4<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("vb", ty.vec3<bool>(), ast::StorageClass::kPrivate);
-    GlobalVar("m", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("arr", ty.array<f32, 10>(), ast::StorageClass::kPrivate);
-    GlobalVar("storage_arr", ty.array<f32>(), ast::StorageClass::kStorage, Group(0_a),
+    GlobalVar("f", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("i", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("u", ty.u32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("vf", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("vf2", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("vi2", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("vf4", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("vb", ty.vec3<bool>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("arr", ty.array<f32, 10>(), ast::AddressSpace::kPrivate);
+    GlobalVar("storage_arr", ty.array<f32>(), ast::AddressSpace::kStorage, Group(0_a),
               Binding(AInt(next_binding++)));
-    GlobalVar("a", ty.atomic(ty.i32()), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    GlobalVar("a", ty.atomic(ty.i32()), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
               Group(0_a), Binding(AInt(next_binding++)));
     if (c.pipeline_stage != ast::PipelineStage::kCompute) {
         GlobalVar("t2d", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), Group(0_a),
diff --git a/src/tint/resolver/source_variable_test.cc b/src/tint/resolver/source_variable_test.cc
index 85d0c31..e662395 100644
--- a/src/tint/resolver/source_variable_test.cc
+++ b/src/tint/resolver/source_variable_test.cc
@@ -26,7 +26,7 @@
 class ResolverSourceVariableTest : public ResolverTest {};
 
 TEST_F(ResolverSourceVariableTest, GlobalPrivateVar) {
-    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -37,7 +37,7 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalWorkgroupVar) {
-    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kWorkgroup);
+    auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kWorkgroup);
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -48,7 +48,7 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalStorageVar) {
-    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kStorage, Group(0_a), Binding(0_a));
+    auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kStorage, Group(0_a), Binding(0_a));
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -59,7 +59,7 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalUniformVar) {
-    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kUniform, Group(0_a), Binding(0_a));
+    auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -71,7 +71,7 @@
 
 TEST_F(ResolverSourceVariableTest, GlobalTextureVar) {
     auto* a = GlobalVar("a", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-                        ast::StorageClass::kNone, Group(0_a), Binding(0_a));
+                        ast::AddressSpace::kNone, Group(0_a), Binding(0_a));
     auto* expr = Expr(a);
     WrapInFunction(Call("textureDimensions", expr));
 
@@ -141,7 +141,7 @@
     // {
     //   let b = a;
     // }
-    auto* param = Param("a", ty.pointer(ty.f32(), ast::StorageClass::kFunction));
+    auto* param = Param("a", ty.pointer(ty.f32(), ast::AddressSpace::kFunction));
     auto* expr_param = Expr(param);
     auto* let = Let("b", expr_param);
     auto* expr_let = Expr("b");
@@ -198,7 +198,7 @@
     // {
     //   a[2i]
     // }
-    auto* a = GlobalVar("a", ty.array(ty.f32(), 4_u), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), 4_u), ast::AddressSpace::kPrivate);
     auto* expr = IndexAccessor(a, 2_i);
     WrapInFunction(expr);
 
@@ -215,7 +215,7 @@
     //   a.f
     // }
     auto* S = Structure("S", utils::Vector{Member("f", ty.f32())});
-    auto* a = GlobalVar("a", ty.Of(S), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.Of(S), ast::AddressSpace::kPrivate);
     auto* expr = MemberAccessor(a, "f");
     WrapInFunction(expr);
 
@@ -231,7 +231,7 @@
     //   let a_ptr1 = &*&a;
     //   let a_ptr2 = &*a_ptr1;
     // }
-    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
     auto* address_of_1 = AddressOf(a);
     auto* deref_1 = Deref(address_of_1);
     auto* address_of_2 = AddressOf(deref_1);
diff --git a/src/tint/resolver/static_assert_test.cc b/src/tint/resolver/static_assert_test.cc
index 3cb67c9..ea7396d 100644
--- a/src/tint/resolver/static_assert_test.cc
+++ b/src/tint/resolver/static_assert_test.cc
@@ -48,14 +48,12 @@
     EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
 }
 
-// TODO(crbug.com/tint/1581): Enable once the '<' operator is implemented for constant evaluation.
-TEST_F(ResolverStaticAssertTest, DISABLED_Global_LessThan_Pass) {
+TEST_F(ResolverStaticAssertTest, Global_LessThan_Pass) {
     GlobalStaticAssert(LessThan(2_i, 3_i));
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-// TODO(crbug.com/tint/1581): Enable once the '<' operator is implemented for constant evaluation.
-TEST_F(ResolverStaticAssertTest, DISABLED_Global_LessThan_Fail) {
+TEST_F(ResolverStaticAssertTest, Global_LessThan_Fail) {
     GlobalStaticAssert(Source{{12, 34}}, LessThan(4_i, 3_i));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
@@ -86,21 +84,19 @@
 }
 
 TEST_F(ResolverStaticAssertTest, Local_NonConst) {
-    GlobalVar("V", ty.bool_(), Expr(true), ast::StorageClass::kPrivate);
+    GlobalVar("V", ty.bool_(), Expr(true), ast::AddressSpace::kPrivate);
     WrapInFunction(StaticAssert(Expr(Source{{12, 34}}, "V")));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: static assertion condition must be a constant expression");
 }
 
-// TODO(crbug.com/tint/1581): Enable once the '<' operator is implemented for constant evaluation.
-TEST_F(ResolverStaticAssertTest, DISABLED_Local_LessThan_Pass) {
+TEST_F(ResolverStaticAssertTest, Local_LessThan_Pass) {
     WrapInFunction(StaticAssert(LessThan(2_i, 3_i)));
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-// TODO(crbug.com/tint/1581): Enable once the '<' operator is implemented for constant evaluation.
-TEST_F(ResolverStaticAssertTest, DISABLED_Local_LessThan_Fail) {
+TEST_F(ResolverStaticAssertTest, Local_LessThan_Fail) {
     WrapInFunction(StaticAssert(Source{{12, 34}}, LessThan(4_i, 3_i)));
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
diff --git a/src/tint/resolver/struct_storage_class_use_test.cc b/src/tint/resolver/struct_address_space_use_test.cc
similarity index 63%
rename from src/tint/resolver/struct_storage_class_use_test.cc
rename to src/tint/resolver/struct_address_space_use_test.cc
index 4d709e3..ff85ce4 100644
--- a/src/tint/resolver/struct_storage_class_use_test.cc
+++ b/src/tint/resolver/struct_address_space_use_test.cc
@@ -25,19 +25,19 @@
 namespace tint::resolver {
 namespace {
 
-using ResolverStorageClassUseTest = ResolverTest;
+using ResolverAddressSpaceUseTest = ResolverTest;
 
-TEST_F(ResolverStorageClassUseTest, UnreachableStruct) {
+TEST_F(ResolverAddressSpaceUseTest, UnreachableStruct) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_TRUE(sem->StorageClassUsage().empty());
+    EXPECT_TRUE(sem->AddressSpaceUsage().empty());
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableFromParameter) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableFromParameter) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     Func("f", utils::Vector{Param("param", ty.Of(s))}, ty.void_(), utils::Empty, utils::Empty);
@@ -46,10 +46,10 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kNone));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kNone));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableFromReturnType) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableFromReturnType) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     Func("f", utils::Empty, ty.Of(s), utils::Vector{Return(Construct(ty.Of(s)))}, utils::Empty);
@@ -58,58 +58,58 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kNone));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kNone));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableFromGlobal) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableFromGlobal) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kPrivate));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalAlias) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableViaGlobalAlias) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = Alias("A", ty.Of(s));
-    GlobalVar("g", ty.Of(a), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(a), ast::AddressSpace::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kPrivate));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalStruct) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableViaGlobalStruct) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* o = Structure("O", utils::Vector{Member("a", ty.Of(s))});
-    GlobalVar("g", ty.Of(o), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(o), ast::AddressSpace::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kPrivate));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableViaGlobalArray) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar("g", a, ast::StorageClass::kPrivate);
+    GlobalVar("g", a, ast::AddressSpace::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kPrivate));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableFromLocal) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableFromLocal) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
 
     WrapInFunction(Var("g", ty.Of(s)));
@@ -118,10 +118,10 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kFunction));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalAlias) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableViaLocalAlias) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = Alias("A", ty.Of(s));
     WrapInFunction(Var("g", ty.Of(a)));
@@ -130,10 +130,10 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kFunction));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalStruct) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableViaLocalStruct) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* o = Structure("O", utils::Vector{Member("a", ty.Of(s))});
     WrapInFunction(Var("g", ty.Of(o)));
@@ -142,10 +142,10 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kFunction));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) {
+TEST_F(ResolverAddressSpaceUseTest, StructReachableViaLocalArray) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
     WrapInFunction(Var("g", a));
@@ -154,13 +154,13 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
+    EXPECT_THAT(sem->AddressSpaceUsage(), UnorderedElementsAre(ast::AddressSpace::kFunction));
 }
 
-TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
+TEST_F(ResolverAddressSpaceUseTest, StructMultipleAddressSpaceUses) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
-    GlobalVar("x", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
-    GlobalVar("y", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("x", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+    GlobalVar("y", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(0_a));
     WrapInFunction(Var("g", ty.Of(s)));
 
@@ -168,9 +168,9 @@
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_THAT(sem->StorageClassUsage(),
-                UnorderedElementsAre(ast::StorageClass::kUniform, ast::StorageClass::kStorage,
-                                     ast::StorageClass::kFunction));
+    EXPECT_THAT(sem->AddressSpaceUsage(),
+                UnorderedElementsAre(ast::AddressSpace::kUniform, ast::AddressSpace::kStorage,
+                                     ast::AddressSpace::kFunction));
 }
 
 }  // namespace
diff --git a/src/tint/resolver/struct_layout_test.cc b/src/tint/resolver/struct_layout_test.cc
index 5b5ab68..1e5ec45 100644
--- a/src/tint/resolver/struct_layout_test.cc
+++ b/src/tint/resolver/struct_layout_test.cc
@@ -498,15 +498,15 @@
 
 TEST_F(ResolverStructLayoutTest, AlignAttributes) {
     auto* inner = Structure("Inner", utils::Vector{
-                                         Member("a", ty.f32(), utils::Vector{MemberAlign(8_u)}),
-                                         Member("b", ty.f32(), utils::Vector{MemberAlign(16_u)}),
-                                         Member("c", ty.f32(), utils::Vector{MemberAlign(4_u)}),
+                                         Member("a", ty.f32(), utils::Vector{MemberAlign(8_i)}),
+                                         Member("b", ty.f32(), utils::Vector{MemberAlign(16_i)}),
+                                         Member("c", ty.f32(), utils::Vector{MemberAlign(4_i)}),
                                      });
     auto* s = Structure("S", utils::Vector{
-                                 Member("a", ty.f32(), utils::Vector{MemberAlign(4_u)}),
-                                 Member("b", ty.u32(), utils::Vector{MemberAlign(8_u)}),
+                                 Member("a", ty.f32(), utils::Vector{MemberAlign(4_i)}),
+                                 Member("b", ty.u32(), utils::Vector{MemberAlign(8_i)}),
                                  Member("c", ty.Of(inner)),
-                                 Member("d", ty.i32(), utils::Vector{MemberAlign(32_u)}),
+                                 Member("d", ty.i32(), utils::Vector{MemberAlign(32_i)}),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -536,7 +536,7 @@
 
 TEST_F(ResolverStructLayoutTest, StructWithLotsOfPadding) {
     auto* s = Structure("S", utils::Vector{
-                                 Member("a", ty.i32(), utils::Vector{MemberAlign(1024_u)}),
+                                 Member("a", ty.i32(), utils::Vector{MemberAlign(1024_i)}),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc
index de64e9a..353e517 100644
--- a/src/tint/resolver/type_constructor_validation_test.cc
+++ b/src/tint/resolver/type_constructor_validation_test.cc
@@ -69,10 +69,10 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_TRUE(TypeOf(a_ident)->Is<sem::Reference>());
     EXPECT_TRUE(TypeOf(a_ident)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
-    EXPECT_EQ(TypeOf(a_ident)->As<sem::Reference>()->StorageClass(), ast::StorageClass::kFunction);
+    EXPECT_EQ(TypeOf(a_ident)->As<sem::Reference>()->AddressSpace(), ast::AddressSpace::kFunction);
     ASSERT_TRUE(TypeOf(b_ident)->Is<sem::Reference>());
     EXPECT_TRUE(TypeOf(b_ident)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
-    EXPECT_EQ(TypeOf(b_ident)->As<sem::Reference>()->StorageClass(), ast::StorageClass::kFunction);
+    EXPECT_EQ(TypeOf(b_ident)->As<sem::Reference>()->AddressSpace(), ast::AddressSpace::kFunction);
 }
 
 using InferTypeTest_FromConstructorExpression = ResolverTestWithParam<Params>;
@@ -96,7 +96,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
     auto* got = TypeOf(a_ident);
     auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
-                                            ast::StorageClass::kFunction, ast::Access::kReadWrite);
+                                            ast::AddressSpace::kFunction, ast::Access::kReadWrite);
     ASSERT_EQ(got, expected) << "got:      " << FriendlyName(got) << "\n"
                              << "expected: " << FriendlyName(expected) << "\n";
 }
@@ -150,7 +150,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
     auto* got = TypeOf(a_ident);
     auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
-                                            ast::StorageClass::kFunction, ast::Access::kReadWrite);
+                                            ast::AddressSpace::kFunction, ast::Access::kReadWrite);
     ASSERT_EQ(got, expected) << "got:      " << FriendlyName(got) << "\n"
                              << "expected: " << FriendlyName(expected) << "\n";
 }
@@ -198,7 +198,7 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
     auto* got = TypeOf(a_ident);
     auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
-                                            ast::StorageClass::kFunction, ast::Access::kReadWrite);
+                                            ast::AddressSpace::kFunction, ast::Access::kReadWrite);
     ASSERT_EQ(got, expected) << "got:      " << FriendlyName(got) << "\n"
                              << "expected: " << FriendlyName(expected) << "\n";
 }
@@ -1968,7 +1968,7 @@
 
 TEST_F(ResolverTypeConstructorValidationTest, Vector_Alias_Argument_Error) {
     auto* alias = Alias("UnsignedInt", ty.u32());
-    GlobalVar("uint_var", ty.Of(alias), ast::StorageClass::kPrivate);
+    GlobalVar("uint_var", ty.Of(alias), ast::AddressSpace::kPrivate);
 
     auto* tc = vec2<f32>(Source{{12, 34}}, "uint_var");
     WrapInFunction(tc);
@@ -1980,8 +1980,8 @@
 TEST_F(ResolverTypeConstructorValidationTest, Vector_Alias_Argument_Success) {
     auto* f32_alias = Alias("Float32", ty.f32());
     auto* vec2_alias = Alias("VectorFloat2", ty.vec2<f32>());
-    GlobalVar("my_f32", ty.Of(f32_alias), ast::StorageClass::kPrivate);
-    GlobalVar("my_vec2", ty.Of(vec2_alias), ast::StorageClass::kPrivate);
+    GlobalVar("my_f32", ty.Of(f32_alias), ast::AddressSpace::kPrivate);
+    GlobalVar("my_vec2", ty.Of(vec2_alias), ast::AddressSpace::kPrivate);
 
     auto* tc = vec3<f32>("my_vec2", "my_f32");
     WrapInFunction(tc);
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 418cae0..4d75155 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -80,14 +80,14 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverTypeValidationTest, GlobalVariableWithStorageClass_Pass) {
+TEST_F(ResolverTypeValidationTest, GlobalVariableWithAddressSpace_Pass) {
     // var<private> global_var: f32;
-    GlobalVar(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "global_var", ty.f32(), ast::AddressSpace::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
+TEST_F(ResolverTypeValidationTest, GlobalConstNoAddressSpace_Pass) {
     // const global_const: f32 = f32();
     GlobalConst(Source{{12, 34}}, "global_const", ty.f32(), Construct(ty.f32()));
 
@@ -98,9 +98,9 @@
     // var global_var0 : f32 = 0.1;
     // var global_var1 : i32 = 0;
 
-    GlobalVar("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1_f));
+    GlobalVar("global_var0", ty.f32(), ast::AddressSpace::kPrivate, Expr(0.1_f));
 
-    GlobalVar(Source{{12, 34}}, "global_var1", ty.f32(), ast::StorageClass::kPrivate, Expr(1_f));
+    GlobalVar(Source{{12, 34}}, "global_var1", ty.f32(), ast::AddressSpace::kPrivate, Expr(1_f));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -116,7 +116,7 @@
              Decl(Var("a", ty.f32(), Expr(2_f))),
          });
 
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate, Expr(2.1_f));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -180,19 +180,19 @@
 
 TEST_F(ResolverTypeValidationTest, ArraySize_AIntLiteral_Pass) {
     // var<private> a : array<f32, 4>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_a)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_a)), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLiteral_Pass) {
     // var<private> a : array<f32, 4u>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_u)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_u)), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Pass) {
     // var<private> a : array<f32, 4i>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_i)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_i)), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -200,7 +200,7 @@
     // const size = 4u;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(4_u));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
@@ -208,129 +208,174 @@
     // const size = 4i;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(4_i));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_AIntLiteral_Zero) {
     // var<private> a : array<f32, 0>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_a)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_a)), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLiteral_Zero) {
     // var<private> a : array<f32, 0u>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_u)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_u)), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Zero) {
     // var<private> a : array<f32, 0i>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_i)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_i)), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Negative) {
     // var<private> a : array<f32, -10i>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, -10_i)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, -10_i)), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (-10) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (-10) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedConst_Zero) {
     // const size = 0u;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(0_u));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedConst_Zero) {
     // const size = 0i;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(0_i));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedConst_Negative) {
     // const size = -10i;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(-10_i));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size (-10) must be greater than 0");
+    EXPECT_EQ(r()->error(), "12:34 error: array count (-10) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_FloatLiteral) {
     // var<private> a : array<f32, 10.0>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 10_f)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 10_f)), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: array size must evaluate to a constant integer expression, but is type "
-              "'f32'");
+    EXPECT_EQ(
+        r()->error(),
+        "12:34 error: array count must evaluate to a constant integer expression, but is type "
+        "'f32'");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_IVecLiteral) {
     // var<private> a : array<f32, vec2<i32>(10, 10)>;
     GlobalVar("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.vec2<i32>(), 10_i, 10_i)),
-              ast::StorageClass::kPrivate);
+              ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: array size must evaluate to a constant integer expression, but is type "
-              "'vec2<i32>'");
+    EXPECT_EQ(
+        r()->error(),
+        "12:34 error: array count must evaluate to a constant integer expression, but is type "
+        "'vec2<i32>'");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_FloatConst) {
     // const size = 10.0;
     // var<private> a : array<f32, size>;
     GlobalConst("size", Expr(10_f));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: array size must evaluate to a constant integer expression, but is type "
-              "'f32'");
+    EXPECT_EQ(
+        r()->error(),
+        "12:34 error: array count must evaluate to a constant integer expression, but is type "
+        "'f32'");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_IVecConst) {
     // const size = vec2<i32>(100, 100);
     // var<private> a : array<f32, size>;
     GlobalConst("size", Construct(ty.vec2<i32>(), 100_i, 100_i));
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: array size must evaluate to a constant integer expression, but is type "
-              "'vec2<i32>'");
+    EXPECT_EQ(
+        r()->error(),
+        "12:34 error: array count must evaluate to a constant integer expression, but is type "
+        "'vec2<i32>'");
+}
+
+TEST_F(ResolverTypeValidationTest, ArraySize_UnderElementCountLimit) {
+    // var<private> a : array<f32, 65535>;
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 65535_a)),
+              ast::AddressSpace::kPrivate);
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, ArraySize_OverElementCountLimit) {
+    // var<private> a : array<f32, 65536>;
+    GlobalVar(Source{{1, 2}}, "a",
+              ty.array(Source{{12, 34}}, ty.f32(), Expr(Source{{12, 34}}, 65536_a)),
+              ast::AddressSpace::kPrivate);
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(1:2 error: array count (65536) must be less than 65536
+1:2 note: while instantiating 'var' a)");
+}
+
+TEST_F(ResolverTypeValidationTest, ArraySize_StorageBufferLargeArray) {
+    // var<storage> a : array<f32, 65536>;
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 65536_a)), ast::AddressSpace::kStorage,
+              utils::Vector{Binding(0_u), Group(0_u)});
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, ArraySize_NestedStorageBufferLargeArray) {
+    // Struct S {
+    //  a : array<f32, 65536>,
+    // }
+    // var<storage> a : S;
+    Structure("S", utils::Vector{Member(Source{{12, 34}}, "a",
+                                        ty.array(Source{{12, 20}}, ty.f32(), 65536_a))});
+    GlobalVar("a", ty.type_name(Source{{12, 30}}, "S"), ast::AddressSpace::kStorage,
+              utils::Vector{Binding(0_u), Group(0_u)});
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_TooBig_ImplicitStride) {
-    // var<private> a : array<f32, 0x40000000u>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0x40000000_u)),
-              ast::StorageClass::kPrivate);
+    // struct S {
+    //   @offset(800000) a : f32
+    // }
+    // var<private> a : array<S, 65535>;
+    Structure("S", utils::Vector{Member(Source{{12, 34}}, "a", ty.f32(),
+                                        utils::Vector{MemberOffset(800000_a)})});
+    GlobalVar("a", ty.array(ty.type_name(Source{{12, 30}}, "S"), Expr(Source{{12, 34}}, 65535_a)),
+              ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size (0x100000000) must not exceed 0xffffffff bytes");
+              "12:34 error: array byte size (0xc34f7cafc) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_TooBig_ExplicitStride) {
-    // var<private> a : @stride(8) array<f32, 0x20000000u>;
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0x20000000_u), 8),
-              ast::StorageClass::kPrivate);
+    // var<private> a : @stride(8000000) array<f32, 65535>;
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 65535_a), 8000000),
+              ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size (0x100000000) must not exceed 0xffffffff bytes");
+              "12:34 error: array byte size (0x7a1185ee00) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_Override_PrivateVar) {
     // override size = 10i;
     // var<private> a : array<f32, size>;
     Override("size", Expr(10_i));
-    GlobalVar("a", ty.array(Source{{12, 34}}, ty.f32(), "size"), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(Source{{12, 34}}, ty.f32(), "size"), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: array with an 'override' element count can only be used as the store "
@@ -342,10 +387,10 @@
     // var<workgroup> a : array<f32, size + 1>;
     Override("size", Expr(10_i));
     GlobalVar("a", ty.array(ty.f32(), Add(Source{{12, 34}}, "size", 1_i)),
-              ast::StorageClass::kWorkgroup);
+              ast::AddressSpace::kWorkgroup);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size must evaluate to a constant integer expression or override "
+              "12:34 error: array count must evaluate to a constant integer expression or override "
               "variable");
 }
 
@@ -354,7 +399,7 @@
     // var<workgroup> a : array<array<f32, size>, 4>;
     Override("size", Expr(10_i));
     GlobalVar("a", ty.array(ty.array(Source{{12, 34}}, ty.f32(), "size"), 4_a),
-              ast::StorageClass::kWorkgroup);
+              ast::AddressSpace::kWorkgroup);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: array with an 'override' element count can only be used as the store "
@@ -413,7 +458,7 @@
     //   var a = w;
     // }
     Override("size", Expr(10_i));
-    GlobalVar("w", ty.array(ty.f32(), "size"), ast::StorageClass::kWorkgroup);
+    GlobalVar("w", ty.array(ty.f32(), "size"), ast::AddressSpace::kWorkgroup);
     Func("f", utils::Empty, ty.void_(),
          utils::Vector{
              Decl(Var("a", Expr(Source{{12, 34}}, "w"))),
@@ -431,7 +476,7 @@
     //   let a = w;
     // }
     Override("size", Expr(10_i));
-    GlobalVar("w", ty.array(ty.f32(), "size"), ast::StorageClass::kWorkgroup);
+    GlobalVar("w", ty.array(ty.f32(), "size"), ast::AddressSpace::kWorkgroup);
     Func("f", utils::Empty, ty.void_(),
          utils::Vector{
              Decl(Let("a", Expr(Source{{12, 34}}, "w"))),
@@ -468,15 +513,15 @@
     // var<workgroup> a : array<f32, size>;
     Override("size", Expr(10_i));
     GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
-              ast::StorageClass::kWorkgroup);
+              ast::AddressSpace::kWorkgroup);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_ModuleVar) {
     // var<private> size : i32 = 10i;
     // var<private> a : array<f32, size>;
-    GlobalVar("size", ty.i32(), Expr(10_i), ast::StorageClass::kPrivate);
-    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("size", ty.i32(), Expr(10_i), ast::AddressSpace::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: var 'size' cannot be referenced at module-scope
@@ -504,7 +549,7 @@
     WrapInFunction(size, a);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size must evaluate to a constant integer expression or override "
+              "12:34 error: array count must evaluate to a constant integer expression or override "
               "variable");
 }
 
@@ -531,7 +576,7 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 12:34 note: while instantiating 'var' a)");
 }
 
@@ -561,33 +606,38 @@
 }
 
 TEST_F(ResolverTypeValidationTest, Struct_TooBig) {
+    // Struct Bar {
+    //   a: array<f32, 10000>;
+    // }
     // struct Foo {
-    //   a: array<f32, 0x20000000>;
-    //   b: array<f32, 0x20000000>;
-    // };
+    //   a: array<Bar, 65535>;
+    //   b: array<Bar, 65535>;
+    // }
 
+    Structure(Source{{10, 34}}, "Bar", utils::Vector{Member("a", ty.array<f32, 10000>())});
     Structure(Source{{12, 34}}, "Foo",
-              utils::Vector{
-                  Member("a", ty.array<f32, 0x20000000>()),
-                  Member("b", ty.array<f32, 0x20000000>()),
-              });
+              utils::Vector{Member("a", ty.array(ty.type_name(Source{{12, 30}}, "Bar"),
+                                                 Expr(Source{{12, 34}}, 65535_a))),
+                            Member("b", ty.array(ty.type_name(Source{{12, 30}}, "Bar"),
+                                                 Expr(Source{{12, 34}}, 65535_a)))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: struct size (0x100000000) must not exceed 0xffffffff bytes");
+              "12:34 error: struct size (0x1387ec780) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, Struct_MemberOffset_TooBig) {
     // struct Foo {
-    //   a: array<f32, 0x3fffffff>;
+    //   @size(2147483647) a: array<f32, 65535>;
     //   b: f32;
     //   c: f32;
     // };
 
     Structure("Foo", utils::Vector{
-                         Member("a", ty.array<f32, 0x3fffffff>()),
-                         Member("b", ty.f32()),
-                         Member(Source{{12, 34}}, "c", ty.f32()),
+                         Member("z", ty.f32(), utils::Vector{MemberSize(2147483647_a)}),
+                         Member("y", ty.f32(), utils::Vector{MemberSize(2147483647_a)}),
+                         Member(Source{{12, 34}}, "a", ty.array<f32, 65535>()),
+                         Member("c", ty.f32()),
                      });
 
     EXPECT_FALSE(r()->Resolve());
@@ -631,7 +681,7 @@
 
     Structure("Foo", utils::Vector{Member("rt", ty.array<f32>())});
     GlobalVar("v", ty.array(ty.type_name(Source{{12, 34}}, "Foo"), 4_u),
-              ast::StorageClass::kPrivate);
+              ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
@@ -676,12 +726,12 @@
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayAsGlobalVariable) {
-    GlobalVar(Source{{56, 78}}, "g", ty.array<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "g", ty.array<i32>(), ast::AddressSpace::kPrivate);
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
 56:78 note: while instantiating 'var' g)");
 }
 
@@ -692,7 +742,7 @@
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
 56:78 note: while instantiating 'var' g)");
 }
 
@@ -717,7 +767,7 @@
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 12:34 note: while instantiating parameter a)");
 }
 
@@ -725,7 +775,7 @@
     // fn func(a : ptr<workgroup, array<u32>>) {}
 
     auto* param =
-        Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::StorageClass::kWorkgroup));
+        Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::AddressSpace::kWorkgroup));
 
     Func("func", utils::Vector{param}, ty.void_(),
          utils::Vector{
@@ -734,7 +784,7 @@
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 12:34 note: while instantiating parameter a)");
 }
 
@@ -774,7 +824,7 @@
 
 TEST_F(ResolverTypeValidationTest, ArrayOfNonStorableType) {
     auto* tex_ty = ty.sampled_texture(Source{{12, 34}}, ast::TextureDimension::k2d, ty.f32());
-    GlobalVar("arr", ty.array(tex_ty, 4_i), ast::StorageClass::kPrivate);
+    GlobalVar("arr", ty.array(tex_ty, 4_i), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -784,8 +834,8 @@
 TEST_F(ResolverTypeValidationTest, VariableAsType) {
     // var<private> a : i32;
     // var<private> b : a;
-    GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.type_name("a"), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.type_name("a"), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -797,7 +847,7 @@
     // fn f() {}
     // var<private> v : f;
     Func("f", utils::Empty, ty.void_(), {});
-    GlobalVar("v", ty.type_name("f"), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.type_name("f"), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -807,7 +857,7 @@
 
 TEST_F(ResolverTypeValidationTest, BuiltinAsType) {
     // var<private> v : max;
-    GlobalVar("v", ty.type_name("max"), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.type_name("max"), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
@@ -818,14 +868,14 @@
     // var<private> v : f16;
     Enable(ast::Extension::kF16);
 
-    GlobalVar("v", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.f16(), ast::AddressSpace::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, F16TypeUsedWithoutExtension) {
     // var<private> v : f16;
-    GlobalVar("v", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.f16(), ast::AddressSpace::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "error: f16 used without 'f16' extension enabled");
@@ -1179,7 +1229,7 @@
     Enable(ast::Extension::kF16);
 
     GlobalVar("a", ty.mat(params.elem_ty(*this), params.columns, params.rows),
-              ast::StorageClass::kPrivate);
+              ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
@@ -1217,7 +1267,7 @@
     Enable(ast::Extension::kF16);
 
     GlobalVar("a", ty.mat(Source{{12, 34}}, params.elem_ty(*this), params.columns, params.rows),
-              ast::StorageClass::kPrivate);
+              ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: matrix element type must be 'f32' or 'f16'");
 }
@@ -1258,7 +1308,7 @@
 
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.vec(params.elem_ty(*this), params.width), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec(params.elem_ty(*this), params.width), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
@@ -1292,7 +1342,7 @@
     Enable(ast::Extension::kF16);
 
     GlobalVar("a", ty.vec(Source{{12, 34}}, params.elem_ty(*this), params.width),
-              ast::StorageClass::kPrivate);
+              ast::AddressSpace::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               "12:34 error: vector element type must be 'bool', 'f32', 'f16', 'i32' "
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index e4c31c9..f75536b 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -1468,14 +1468,14 @@
             [&](const ast::IdentifierExpression* ident) {
                 std::string var_type = "";
                 auto* var = sem_.Get<sem::VariableUser>(ident)->Variable();
-                switch (var->StorageClass()) {
-                    case ast::StorageClass::kStorage:
+                switch (var->AddressSpace()) {
+                    case ast::AddressSpace::kStorage:
                         var_type = "read_write storage buffer ";
                         break;
-                    case ast::StorageClass::kWorkgroup:
+                    case ast::AddressSpace::kWorkgroup:
                         var_type = "workgroup storage variable ";
                         break;
-                    case ast::StorageClass::kPrivate:
+                    case ast::AddressSpace::kPrivate:
                         var_type = "module-scope private variable ";
                         break;
                     default:
@@ -1548,9 +1548,8 @@
         // Helper to produce a diagnostic message with the severity required by this invocation of
         // the `MakeError` function.
         auto report = [&](Source source, std::string msg) {
-            // TODO(jrprice): Switch to error instead of warning when feedback has settled.
             diag::Diagnostic error{};
-            error.severity = note ? diag::Severity::Note : diag::Severity::Warning;
+            error.severity = note ? diag::Severity::Note : diag::Severity::Error;
             error.system = diag::System::Resolver;
             error.source = source;
             error.message = msg;
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index e2e2796..52cccbe 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -51,8 +51,7 @@
                 EXPECT_EQ(program.Diagnostics().count(), 0u) << error_;
             }
         } else {
-            // TODO(jrprice): expect false when uniformity issues become errors.
-            EXPECT_TRUE(valid) << error_;
+            EXPECT_FALSE(valid);
         }
     }
 
@@ -318,7 +317,7 @@
     bool should_pass = !(MayBeNonUniform(condition) && RequiredToBeUniform(function));
     RunTest(src, should_pass);
     if (!should_pass) {
-        EXPECT_THAT(error_, ::testing::StartsWith("test:31:5 warning: "));
+        EXPECT_THAT(error_, ::testing::StartsWith("test:31:5 error: "));
         EXPECT_THAT(error_, ::testing::HasSubstr("must only be called from uniform control flow"));
     }
 }
@@ -391,7 +390,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:17:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -440,7 +439,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:21:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:21:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -528,7 +527,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:11:7 warning: parameter 'i' of 'foo' must be uniform
+              R"(test:11:7 error: parameter 'i' of 'foo' must be uniform
   foo(rw);
       ^^
 
@@ -581,7 +580,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -646,7 +645,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:17:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -697,7 +696,7 @@
     if (!should_pass) {
         EXPECT_EQ(
             error_,
-            R"(test:5:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+            R"(test:5:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -732,7 +731,7 @@
     if (!should_pass) {
         EXPECT_EQ(
             error_,
-            R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+            R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -777,7 +776,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -808,7 +807,7 @@
     RunTest(src, should_pass);
     if (!should_pass) {
         EXPECT_EQ(error_,
-                  R"(test:5:5 warning: 'dpdx' must only be called from uniform control flow
+                  R"(test:5:5 error: 'dpdx' must only be called from uniform control flow
     dpdx(0.5);
     ^^^^
 
@@ -842,7 +841,7 @@
     RunTest(src, should_pass);
     if (!should_pass) {
         EXPECT_EQ(error_,
-                  R"(test:9:5 warning: 'dpdx' must only be called from uniform control flow
+                  R"(test:9:5 error: 'dpdx' must only be called from uniform control flow
     dpdx(0.5);
     ^^^^
 
@@ -879,7 +878,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:5:5 warning: 'dpdx' must only be called from uniform control flow
+              R"(test:5:5 error: 'dpdx' must only be called from uniform control flow
     dpdx(0.5);
     ^^^^
 
@@ -909,7 +908,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'dpdx' must only be called from uniform control flow
+              R"(test:9:5 error: 'dpdx' must only be called from uniform control flow
     dpdx(0.5);
     ^^^^
 
@@ -1025,7 +1024,7 @@
         EXPECT_THAT(
             error_,
             ::testing::StartsWith(
-                R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+                R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();)"));
         EXPECT_THAT(error_,
                     ::testing::HasSubstr("test:14:9 note: reading from read_write storage buffer "
@@ -1065,7 +1064,7 @@
         EXPECT_THAT(
             error_,
             ::testing::StartsWith(
-                R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+                R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();)"));
         EXPECT_THAT(error_,
                     ::testing::HasSubstr("test:13:9 note: reading from read_write storage buffer "
@@ -1106,7 +1105,7 @@
         EXPECT_THAT(
             error_,
             ::testing::StartsWith(
-                R"(test:15:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+                R"(test:15:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();)"));
         EXPECT_THAT(error_,
                     ::testing::HasSubstr("test:13:9 note: reading from read_write storage buffer "
@@ -1157,7 +1156,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1212,7 +1211,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -1282,7 +1281,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -1366,7 +1365,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -1404,7 +1403,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1447,7 +1446,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:20:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1521,7 +1520,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:20:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1562,7 +1561,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -1608,7 +1607,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:16:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:16:9 error: 'workgroupBarrier' must only be called from uniform control flow
         workgroupBarrier();
         ^^^^^^^^^^^^^^^^
 
@@ -1649,7 +1648,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -1690,7 +1689,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -1826,7 +1825,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -1871,7 +1870,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1906,7 +1905,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1945,7 +1944,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -1982,7 +1981,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2037,7 +2036,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2092,7 +2091,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2153,7 +2152,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2198,7 +2197,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2240,7 +2239,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2281,7 +2280,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2342,7 +2341,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2379,7 +2378,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2444,7 +2443,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:17:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:17:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2491,7 +2490,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:23:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:23:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2534,7 +2533,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2577,7 +2576,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -2672,7 +2671,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2700,7 +2699,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2729,7 +2728,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2758,7 +2757,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2787,7 +2786,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2816,7 +2815,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2844,7 +2843,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2875,7 +2874,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -2952,7 +2951,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3035,7 +3034,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3072,7 +3071,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3229,7 +3228,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -3259,7 +3258,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -3296,7 +3295,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -3326,7 +3325,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -3361,7 +3360,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:11:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:11:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -3428,7 +3427,7 @@
       fallthrough;
       ^^^^^^^^^^^
 
-test:14:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+test:14:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -3498,7 +3497,7 @@
       fallthrough;
       ^^^^^^^^^^^
 
-test:14:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+test:14:9 error: 'workgroupBarrier' must only be called from uniform control flow
         workgroupBarrier();
         ^^^^^^^^^^^^^^^^
 
@@ -3537,7 +3536,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:9 error: 'workgroupBarrier' must only be called from uniform control flow
         workgroupBarrier();
         ^^^^^^^^^^^^^^^^
 
@@ -3577,7 +3576,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:19:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:19:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3643,7 +3642,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3712,7 +3711,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3754,7 +3753,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -3892,7 +3891,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:17:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -3929,7 +3928,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -3959,7 +3958,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4024,7 +4023,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4052,7 +4051,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4081,7 +4080,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4113,7 +4112,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:7 warning: parameter 'p' of 'bar' must be uniform
+              R"(test:12:7 error: parameter 'p' of 'bar' must be uniform
   bar(&v);
       ^
 
@@ -4187,7 +4186,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4235,7 +4234,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4265,7 +4264,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4297,7 +4296,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4348,7 +4347,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4398,7 +4397,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4451,7 +4450,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4485,7 +4484,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4568,7 +4567,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4662,7 +4661,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4699,7 +4698,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4736,7 +4735,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4781,7 +4780,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:24:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:24:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4820,7 +4819,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4856,7 +4855,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
       workgroupBarrier();
       ^^^^^^^^^^^^^^^^
 
@@ -4892,7 +4891,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4929,7 +4928,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -4966,7 +4965,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5007,7 +5006,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:20:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5066,7 +5065,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5123,7 +5122,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5178,7 +5177,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5216,7 +5215,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5256,7 +5255,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5292,7 +5291,7 @@
     foo_body.Push(b.Decl(b.Let("rhs", rhs_init)));
     for (int i = 0; i < 255; i++) {
         params.Push(
-            b.Param("p" + std::to_string(i), ty.pointer(ty.i32(), ast::StorageClass::kFunction)));
+            b.Param("p" + std::to_string(i), ty.pointer(ty.i32(), ast::AddressSpace::kFunction)));
         if (i > 0) {
             foo_body.Push(b.Assign(b.Deref("p" + std::to_string(i)), "rhs"));
         }
@@ -5311,7 +5310,7 @@
     //     workgroupBarrier();
     //   }
     // }
-    b.GlobalVar("non_uniform_global", ty.i32(), ast::StorageClass::kPrivate);
+    b.GlobalVar("non_uniform_global", ty.i32(), ast::AddressSpace::kPrivate);
     utils::Vector<const ast::Statement*, 8> main_body;
     utils::Vector<const ast::Expression*, 8> args;
     for (int i = 0; i < 255; i++) {
@@ -5324,10 +5323,9 @@
     main_body.Push(b.If(b.Equal("v254", 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
     b.Func("main", utils::Empty, ty.void_(), main_body);
 
-    // TODO(jrprice): Expect false when uniformity issues become errors.
-    EXPECT_TRUE(RunTest(std::move(b))) << error_;
+    EXPECT_FALSE(RunTest(std::move(b)));
     EXPECT_EQ(error_,
-              R"(warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(error: 'workgroupBarrier' must only be called from uniform control flow
 note: control flow depends on non-uniform value
 note: reading from module-scope private variable 'non_uniform_global' may result in a non-uniform value)");
 }
@@ -5363,7 +5361,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5392,7 +5390,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5437,7 +5435,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5470,7 +5468,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5503,7 +5501,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5532,7 +5530,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5581,7 +5579,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5614,7 +5612,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5667,7 +5665,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5704,7 +5702,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5741,7 +5739,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5782,7 +5780,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5811,7 +5809,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5856,7 +5854,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5886,7 +5884,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5919,7 +5917,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5952,7 +5950,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -5986,7 +5984,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6040,7 +6038,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6067,7 +6065,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6094,7 +6092,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6124,7 +6122,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6154,7 +6152,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6188,7 +6186,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:14:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:14:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -6227,7 +6225,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -6266,7 +6264,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -6305,7 +6303,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:3 error: 'workgroupBarrier' must only be called from uniform control flow
   workgroupBarrier();
   ^^^^^^^^^^^^^^^^
 
@@ -6448,7 +6446,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6475,7 +6473,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:6:5 warning: 'storageBarrier' must only be called from uniform control flow
+              R"(test:6:5 error: 'storageBarrier' must only be called from uniform control flow
     storageBarrier();
     ^^^^^^^^^^^^^^
 
@@ -6521,7 +6519,7 @@
     //     workgroupBarrier();
     //   }
     // }
-    b.GlobalVar("v0", ty.i32(), ast::StorageClass::kPrivate, b.Expr(0_i));
+    b.GlobalVar("v0", ty.i32(), ast::AddressSpace::kPrivate, b.Expr(0_i));
     utils::Vector<const ast::Statement*, 8> foo_body;
     std::string v_last = "v0";
     for (int i = 1; i < 100000; i++) {
@@ -6532,10 +6530,9 @@
     foo_body.Push(b.If(b.Equal(v_last, 0_i), b.Block(b.CallStmt(b.Call("workgroupBarrier")))));
     b.Func("foo", utils::Empty, ty.void_(), foo_body);
 
-    // TODO(jrprice): Expect false when uniformity issues become errors.
-    EXPECT_TRUE(RunTest(std::move(b))) << error_;
+    EXPECT_FALSE(RunTest(std::move(b)));
     EXPECT_EQ(error_,
-              R"(warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(error: 'workgroupBarrier' must only be called from uniform control flow
 note: control flow depends on non-uniform value
 note: reading from module-scope private variable 'v0' may result in a non-uniform value)");
 }
@@ -6561,7 +6558,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:10:5 warning: 'foo' must only be called from uniform control flow
+              R"(test:10:5 error: 'foo' must only be called from uniform control flow
     foo();
     ^^^
 
@@ -6604,7 +6601,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:18:5 warning: 'foo' must only be called from uniform control flow
+              R"(test:18:5 error: 'foo' must only be called from uniform control flow
     foo();
     ^^^
 
@@ -6647,7 +6644,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:19:7 warning: parameter 'c' of 'foo' must be uniform
+              R"(test:19:7 error: parameter 'c' of 'foo' must be uniform
   foo(non_uniform);
       ^^^^^^^^^^^
 
@@ -6694,7 +6691,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6732,7 +6729,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6774,7 +6771,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
@@ -6820,7 +6817,7 @@
 
     RunTest(src, false);
     EXPECT_EQ(error_,
-              R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+              R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
     workgroupBarrier();
     ^^^^^^^^^^^^^^^^
 
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 90b1664..f4fa3cc 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -61,8 +61,8 @@
 };
 
 TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInVertexStage) {
-    GlobalVar(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("dst", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
     Func(Source{{9, 10}}, "f0", utils::Empty, ty.vec4<f32>(),
@@ -93,8 +93,8 @@
     //  f1();
     //}
 
-    GlobalVar(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("dst", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
     Func(Source{{5, 6}}, "f2", utils::Empty, ty.void_(), utils::Vector{stmt});
@@ -226,7 +226,7 @@
     //   return;
     // }
 
-    GlobalVar("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar("global_var", ty.f32(), ast::AddressSpace::kPrivate, Expr(2.1_f));
 
     Func("my_func", utils::Empty, ty.void_(),
          utils::Vector{
@@ -299,8 +299,8 @@
     EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
 }
 
-TEST_F(ResolverValidationTest, StorageClass_FunctionVariableWorkgroupClass) {
-    auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
+TEST_F(ResolverValidationTest, AddressSpace_FunctionVariableWorkgroupClass) {
+    auto* var = Var("var", ty.i32(), ast::AddressSpace::kWorkgroup);
 
     Func("func", utils::Empty, ty.void_(),
          utils::Vector{
@@ -310,11 +310,11 @@
     EXPECT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "error: function-scope 'var' declaration must use 'function' storage class");
+              "error: function-scope 'var' declaration must use 'function' address space");
 }
 
-TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
-    auto* var = Var("s", ty.i32(), ast::StorageClass::kPrivate);
+TEST_F(ResolverValidationTest, AddressSpace_FunctionVariableI32) {
+    auto* var = Var("s", ty.i32(), ast::AddressSpace::kPrivate);
 
     Func("func", utils::Empty, ty.void_(),
          utils::Vector{
@@ -324,31 +324,31 @@
     EXPECT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              "error: function-scope 'var' declaration must use 'function' storage class");
+              "error: function-scope 'var' declaration must use 'function' address space");
 }
 
-TEST_F(ResolverValidationTest, StorageClass_SamplerExplicitStorageClass) {
+TEST_F(ResolverValidationTest, AddressSpace_SamplerExplicitAddressSpace) {
     auto* t = ty.sampler(ast::SamplerKind::kSampler);
-    GlobalVar(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle, Binding(0_a), Group(0_a));
+    GlobalVar(Source{{12, 34}}, "var", t, ast::AddressSpace::kHandle, Binding(0_a), Group(0_a));
 
     EXPECT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: variables of type 'sampler' must not have a storage class)");
+              R"(12:34 error: variables of type 'sampler' must not have a address space)");
 }
 
-TEST_F(ResolverValidationTest, StorageClass_TextureExplicitStorageClass) {
+TEST_F(ResolverValidationTest, AddressSpace_TextureExplicitAddressSpace) {
     auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
-    GlobalVar(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle, Binding(0_a), Group(0_a));
+    GlobalVar(Source{{12, 34}}, "var", t, ast::AddressSpace::kHandle, Binding(0_a), Group(0_a));
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
 
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: variables of type 'texture_1d<f32>' must not have a storage class)");
+              R"(12:34 error: variables of type 'texture_1d<f32>' must not have a address space)");
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
-    GlobalVar("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "xyqz");
 
@@ -360,7 +360,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
-    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
 
     auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "rgyw");
 
@@ -373,7 +373,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
-    GlobalVar("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* ident = Expr(Source{{{3, 3}, {3, 8}}}, "zzzzz");
     auto* mem = MemberAccessor("my_vec", ident);
@@ -384,7 +384,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadIndex) {
-    GlobalVar("my_vec", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* ident = Expr(Source{{3, 3}}, "z");
     auto* mem = MemberAccessor("my_vec", ident);
@@ -417,7 +417,7 @@
     //     let x: f32 = (*p).z;
     //     return x;
     // }
-    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
+    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction));
     auto* star_p = Deref(p);
     auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
     auto* accessor_expr = MemberAccessor(star_p, z);
@@ -435,7 +435,7 @@
     //     let x: f32 = *p.z;
     //     return x;
     // }
-    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
+    auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction));
     auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
     auto* accessor_expr = MemberAccessor(p, z);
     auto* star_p = Deref(accessor_expr);
@@ -1237,22 +1237,31 @@
     EXPECT_TRUE(r()->Resolve());
 }
 
-TEST_F(ResolverValidationTest, NonPOTStructMemberAlignAttribute) {
+TEST_F(ResolverValidationTest, NegativeStructMemberAlignAttribute) {
     Structure("S", utils::Vector{
-                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, 3_u)}),
+                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, -2_i)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: align value must be a positive, power-of-two integer");
+    EXPECT_EQ(r()->error(), "12:34 error: 'align' value must be a positive, power-of-two integer");
+}
+
+TEST_F(ResolverValidationTest, NonPOTStructMemberAlignAttribute) {
+    Structure("S", utils::Vector{
+                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, 3_i)}),
+                   });
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: 'align' value must be a positive, power-of-two integer");
 }
 
 TEST_F(ResolverValidationTest, ZeroStructMemberAlignAttribute) {
     Structure("S", utils::Vector{
-                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, 0_u)}),
+                       Member("a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, 0_i)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: align value must be a positive, power-of-two integer");
+    EXPECT_EQ(r()->error(), "12:34 error: 'align' value must be a positive, power-of-two integer");
 }
 
 TEST_F(ResolverValidationTest, ZeroStructMemberSizeAttribute) {
@@ -1279,7 +1288,7 @@
 TEST_F(ResolverValidationTest, OffsetAndAlignAttribute) {
     Structure("S", utils::Vector{
                        Member(Source{{12, 34}}, "a", ty.f32(),
-                              utils::Vector{MemberOffset(0_a), MemberAlign(4_u)}),
+                              utils::Vector{MemberOffset(0_a), MemberAlign(4_i)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1291,7 +1300,7 @@
 TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeAttribute) {
     Structure("S", utils::Vector{
                        Member(Source{{12, 34}}, "a", ty.f32(),
-                              utils::Vector{MemberOffset(0_a), MemberAlign(4_u), MemberSize(4_a)}),
+                              utils::Vector{MemberOffset(0_a), MemberAlign(4_i), MemberSize(4_a)}),
                    });
 
     EXPECT_FALSE(r()->Resolve());
@@ -1303,8 +1312,8 @@
 TEST_F(ResolverTest, Expr_Constructor_Cast_Pointer) {
     auto* vf = Var("vf", ty.f32());
     auto* c =
-        Construct(Source{{12, 34}}, ty.pointer<i32>(ast::StorageClass::kFunction), ExprList(vf));
-    auto* ip = Let("ip", ty.pointer<i32>(ast::StorageClass::kFunction), c);
+        Construct(Source{{12, 34}}, ty.pointer<i32>(ast::AddressSpace::kFunction), ExprList(vf));
+    auto* ip = Let("ip", ty.pointer<i32>(ast::AddressSpace::kFunction), c);
     WrapInFunction(Decl(vf), Decl(ip));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index c1f52fb..36ac610 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -322,7 +322,7 @@
 }
 
 bool Validator::VariableInitializer(const ast::Variable* v,
-                                    ast::StorageClass storage_class,
+                                    ast::AddressSpace address_space,
                                     const sem::Type* storage_ty,
                                     const sem::Expression* initializer) const {
     auto* initializer_ty = initializer->Type();
@@ -338,17 +338,17 @@
     }
 
     if (v->Is<ast::Var>()) {
-        switch (storage_class) {
-            case ast::StorageClass::kPrivate:
-            case ast::StorageClass::kFunction:
+        switch (address_space) {
+            case ast::AddressSpace::kPrivate:
+            case ast::AddressSpace::kFunction:
                 break;  // Allowed an initializer
             default:
                 // https://gpuweb.github.io/gpuweb/wgsl/#var-and-let
                 // Optionally has an initializer expression, if the variable is in the
-                // private or function storage classes.
-                AddError("var of storage class '" + utils::ToString(storage_class) +
+                // private or function address spacees.
+                AddError("var of address space '" + utils::ToString(address_space) +
                              "' cannot have an initializer. var initializers are only "
-                             "supported for the storage classes "
+                             "supported for the address spacees "
                              "'private' and 'function'",
                          v->source);
                 return false;
@@ -358,18 +358,19 @@
     return true;
 }
 
-bool Validator::StorageClassLayout(const sem::Type* store_ty,
-                                   ast::StorageClass sc,
+bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
+                                   ast::AddressSpace address_space,
                                    Source source,
                                    ValidTypeStorageLayouts& layouts) const {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class-layout-constraints
 
-    auto is_uniform_struct_or_array = [sc](const sem::Type* ty) {
-        return sc == ast::StorageClass::kUniform && ty->IsAnyOf<sem::Array, sem::Struct>();
+    auto is_uniform_struct_or_array = [address_space](const sem::Type* ty) {
+        return address_space == ast::AddressSpace::kUniform &&
+               ty->IsAnyOf<sem::Array, sem::Struct>();
     };
 
-    auto is_uniform_struct = [sc](const sem::Type* ty) {
-        return sc == ast::StorageClass::kUniform && ty->Is<sem::Struct>();
+    auto is_uniform_struct = [address_space](const sem::Type* ty) {
+        return address_space == ast::AddressSpace::kUniform && ty->Is<sem::Struct>();
     };
 
     auto required_alignment_of = [&](const sem::Type* ty) {
@@ -385,22 +386,22 @@
         return symbols_.NameFor(sm->Declaration()->symbol);
     };
 
-    // Cache result of type + storage class pair.
-    if (!layouts.emplace(store_ty, sc).second) {
+    // Cache result of type + address space pair.
+    if (!layouts.emplace(store_ty, address_space).second) {
         return true;
     }
 
-    if (!ast::IsHostShareable(sc)) {
+    if (!ast::IsHostShareable(address_space)) {
         return true;
     }
 
-    // Temporally forbid using f16 types in "uniform" and "storage" storage class.
+    // Temporally forbid using f16 types in "uniform" and "storage" address space.
     // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in "uniform" and
-    // "storage" storage class but keep for "push_constant" storage class.
+    // "storage" address space but keep for "push_constant" address space.
     if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty))) {
-        AddError(
-            "using f16 types in '" + utils::ToString(sc) + "' storage class is not implemented yet",
-            source);
+        AddError("using f16 types in '" + utils::ToString(address_space) +
+                     "' address space is not implemented yet",
+                 source);
         return false;
     }
 
@@ -410,7 +411,8 @@
             uint32_t required_align = required_alignment_of(m->Type());
 
             // Recurse into the member type.
-            if (!StorageClassLayout(m->Type(), sc, m->Declaration()->type->source, layouts)) {
+            if (!AddressSpaceLayout(m->Type(), address_space, m->Declaration()->type->source,
+                                    layouts)) {
                 AddNote("see layout of struct:\n" + str->Layout(symbols_),
                         str->Declaration()->source);
                 return false;
@@ -420,7 +422,7 @@
             if (m->Offset() % required_align != 0) {
                 AddError("the offset of a struct member of type '" +
                              m->Type()->UnwrapRef()->FriendlyName(symbols_) +
-                             "' in storage class '" + utils::ToString(sc) +
+                             "' in address space '" + utils::ToString(address_space) +
                              "' must be a multiple of " + std::to_string(required_align) +
                              " bytes, but '" + member_name_of(m) + "' is currently at offset " +
                              std::to_string(m->Offset()) + ". Consider setting @align(" +
@@ -474,11 +476,11 @@
         // TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested
         // element type here, but we can't easily get that from the semantic node.
         // We should consider recursing through the AST type nodes instead.
-        if (!StorageClassLayout(arr->ElemType(), sc, source, layouts)) {
+        if (!AddressSpaceLayout(arr->ElemType(), address_space, source, layouts)) {
             return false;
         }
 
-        if (sc == ast::StorageClass::kUniform) {
+        if (address_space == ast::AddressSpace::kUniform) {
             // We already validated that this array member is itself aligned to 16
             // bytes above, so we only need to validate that stride is a multiple
             // of 16 bytes.
@@ -516,22 +518,22 @@
     return true;
 }
 
-bool Validator::StorageClassLayout(const sem::Variable* var,
+bool Validator::AddressSpaceLayout(const sem::Variable* var,
                                    const ast::Extensions& enabled_extensions,
                                    ValidTypeStorageLayouts& layouts) const {
-    if (var->StorageClass() == ast::StorageClass::kPushConstant &&
+    if (var->AddressSpace() == ast::AddressSpace::kPushConstant &&
         !enabled_extensions.Contains(ast::Extension::kChromiumExperimentalPushConstant) &&
         IsValidationEnabled(var->Declaration()->attributes,
-                            ast::DisabledValidation::kIgnoreStorageClass)) {
+                            ast::DisabledValidation::kIgnoreAddressSpace)) {
         AddError(
-            "use of variable storage class 'push_constant' requires enabling extension "
+            "use of variable address space 'push_constant' requires enabling extension "
             "'chromium_experimental_push_constant'",
             var->Declaration()->source);
         return false;
     }
 
     if (auto* str = var->Type()->UnwrapRef()->As<sem::Struct>()) {
-        if (!StorageClassLayout(str, var->StorageClass(), str->Declaration()->source, layouts)) {
+        if (!AddressSpaceLayout(str, var->AddressSpace(), str->Declaration()->source, layouts)) {
             AddNote("see declaration of variable", var->Declaration()->source);
             return false;
         }
@@ -540,7 +542,7 @@
         if (var->Declaration()->type) {
             source = var->Declaration()->type->source;
         }
-        if (!StorageClassLayout(var->Type()->UnwrapRef(), var->StorageClass(), source, layouts)) {
+        if (!AddressSpaceLayout(var->Type()->UnwrapRef(), var->AddressSpace(), source, layouts)) {
             return false;
         }
     }
@@ -559,7 +561,7 @@
         decl,  //
         [&](const ast::Var* var) {
             if (IsValidationEnabled(var->attributes,
-                                    ast::DisabledValidation::kIgnoreStorageClass)) {
+                                    ast::DisabledValidation::kIgnoreAddressSpace)) {
                 if (!local->Type()->UnwrapRef()->IsConstructible()) {
                     AddError("function-scope 'var' must have a constructible type",
                              var->type ? var->type->source : var->source);
@@ -583,7 +585,7 @@
     const std::unordered_map<OverrideId, const sem::Variable*>& override_ids,
     const std::unordered_map<const sem::Type*, const Source&>& atomic_composite_info) const {
     auto* decl = global->Declaration();
-    if (global->StorageClass() != ast::StorageClass::kWorkgroup &&
+    if (global->AddressSpace() != ast::AddressSpace::kWorkgroup &&
         IsArrayWithOverrideCount(global->Type())) {
         RaiseArrayWithOverrideCountError(decl->type ? decl->type->source
                                                     : decl->constructor->source);
@@ -599,8 +601,8 @@
                 return false;
             }
 
-            if (global->StorageClass() == ast::StorageClass::kNone) {
-                AddError("module-scope 'var' declaration must have a storage class", decl->source);
+            if (global->AddressSpace() == ast::AddressSpace::kNone) {
+                AddError("module-scope 'var' declaration must have a address space", decl->source);
                 return false;
             }
 
@@ -608,11 +610,11 @@
                 bool is_shader_io_attribute =
                     attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
                                   ast::InvariantAttribute, ast::LocationAttribute>();
-                bool has_io_storage_class = global->StorageClass() == ast::StorageClass::kIn ||
-                                            global->StorageClass() == ast::StorageClass::kOut;
+                bool has_io_address_space = global->AddressSpace() == ast::AddressSpace::kIn ||
+                                            global->AddressSpace() == ast::AddressSpace::kOut;
                 if (!attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
                                    ast::InternalAttribute>() &&
-                    (!is_shader_io_attribute || !has_io_storage_class)) {
+                    (!is_shader_io_attribute || !has_io_address_space)) {
                     AddError("attribute '" + attr->Name() + "' is not valid for module-scope 'var'",
                              attr->source);
                     return false;
@@ -621,9 +623,9 @@
 
             // 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.
+            // storage address space, must not be written.
             if (var->declared_access != ast::Access::kUndefined) {
-                if (global->StorageClass() == ast::StorageClass::kStorage) {
+                if (global->AddressSpace() == ast::AddressSpace::kStorage) {
                     // The access mode for the storage address space can only be 'read' or
                     // 'read_write'.
                     if (var->declared_access == ast::Access::kWrite) {
@@ -632,7 +634,7 @@
                         return false;
                     }
                 } else {
-                    AddError("only variables in <storage> storage class may declare an access mode",
+                    AddError("only variables in <storage> address space may declare an access mode",
                              decl->source);
                     return false;
                 }
@@ -672,15 +674,15 @@
         return false;
     }
 
-    if (global->StorageClass() == ast::StorageClass::kFunction) {
-        AddError("module-scope 'var' must not use storage class 'function'", decl->source);
+    if (global->AddressSpace() == ast::AddressSpace::kFunction) {
+        AddError("module-scope 'var' must not use address space 'function'", decl->source);
         return false;
     }
 
-    switch (global->StorageClass()) {
-        case ast::StorageClass::kUniform:
-        case ast::StorageClass::kStorage:
-        case ast::StorageClass::kHandle: {
+    switch (global->AddressSpace()) {
+        case ast::AddressSpace::kUniform:
+        case ast::AddressSpace::kStorage:
+        case ast::AddressSpace::kHandle: {
             // https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
             // Each resource variable must be declared with both group and binding
             // attributes.
@@ -712,29 +714,32 @@
 bool Validator::AtomicVariable(
     const sem::Variable* var,
     std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const {
-    auto sc = var->StorageClass();
+    auto address_space = var->AddressSpace();
     auto* decl = var->Declaration();
     auto access = var->Access();
     auto* type = var->Type()->UnwrapRef();
     auto source = decl->type ? decl->type->source : decl->source;
 
     if (type->Is<sem::Atomic>()) {
-        if (sc != ast::StorageClass::kWorkgroup && sc != ast::StorageClass::kStorage) {
-            AddError("atomic variables must have <storage> or <workgroup> storage class", source);
+        if (address_space != ast::AddressSpace::kWorkgroup &&
+            address_space != ast::AddressSpace::kStorage) {
+            AddError("atomic variables must have <storage> or <workgroup> address space", source);
             return false;
         }
     } else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
         auto found = atomic_composite_info.find(type);
         if (found != atomic_composite_info.end()) {
-            if (sc != ast::StorageClass::kStorage && sc != ast::StorageClass::kWorkgroup) {
-                AddError("atomic variables must have <storage> or <workgroup> storage class",
+            if (address_space != ast::AddressSpace::kStorage &&
+                address_space != ast::AddressSpace::kWorkgroup) {
+                AddError("atomic variables must have <storage> or <workgroup> address space",
                          source);
                 AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
                         found->second);
                 return false;
-            } else if (sc == ast::StorageClass::kStorage && access != ast::Access::kReadWrite) {
+            } else if (address_space == ast::AddressSpace::kStorage &&
+                       access != ast::Access::kReadWrite) {
                 AddError(
-                    "atomic variables in <storage> storage class must have read_write "
+                    "atomic variables in <storage> address space must have read_write "
                     "access mode",
                     source);
                 AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
@@ -756,21 +761,21 @@
         return false;
     }
 
-    if (storage_ty->is_handle() && var->declared_storage_class != ast::StorageClass::kNone) {
+    if (storage_ty->is_handle() && var->declared_address_space != ast::AddressSpace::kNone) {
         // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
         // If the store type is a texture type or a sampler type, then the
-        // variable declaration must not have a storage class attribute. The
-        // storage class will always be handle.
+        // variable declaration must not have a address space attribute. The
+        // address space will always be handle.
         AddError(
-            "variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a storage class",
+            "variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a address space",
             var->source);
         return false;
     }
 
-    if (IsValidationEnabled(var->attributes, ast::DisabledValidation::kIgnoreStorageClass) &&
-        (var->declared_storage_class == ast::StorageClass::kIn ||
-         var->declared_storage_class == ast::StorageClass::kOut)) {
-        AddError("invalid use of input/output storage class", var->source);
+    if (IsValidationEnabled(var->attributes, ast::DisabledValidation::kIgnoreAddressSpace) &&
+        (var->declared_address_space == ast::AddressSpace::kIn ||
+         var->declared_address_space == ast::AddressSpace::kOut)) {
+        AddError("invalid use of input/output address space", var->source);
         return false;
     }
     return true;
@@ -873,12 +878,14 @@
     }
 
     if (auto* ref = var->Type()->As<sem::Pointer>()) {
-        auto sc = ref->StorageClass();
-        if (!(sc == ast::StorageClass::kFunction || sc == ast::StorageClass::kPrivate ||
-              sc == ast::StorageClass::kWorkgroup) &&
-            IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreStorageClass)) {
+        auto address_space = ref->AddressSpace();
+        if (!(address_space == ast::AddressSpace::kFunction ||
+              address_space == ast::AddressSpace::kPrivate ||
+              address_space == ast::AddressSpace::kWorkgroup) &&
+            IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
             std::stringstream ss;
-            ss << "function parameter of pointer type cannot be in '" << sc << "' storage class";
+            ss << "function parameter of pointer type cannot be in '" << address_space
+               << "' address space";
             AddError(ss.str(), decl->source);
             return false;
         }
@@ -1954,7 +1961,7 @@
         auto stage = entry_point->Declaration()->PipelineStage();
         if (stage != ast::PipelineStage::kCompute) {
             for (auto* var : func->DirectlyReferencedGlobals()) {
-                if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
+                if (var->AddressSpace() == ast::AddressSpace::kWorkgroup) {
                     std::stringstream stage_name;
                     stage_name << stage;
                     for (auto* user : var->Users()) {
@@ -2042,7 +2049,7 @@
 
         auto check_push_constant = [&](const sem::Function* func, const sem::Function* ep) {
             for (auto* var : func->DirectlyReferencedGlobals()) {
-                if (var->StorageClass() != ast::StorageClass::kPushConstant ||
+                if (var->AddressSpace() != ast::AddressSpace::kPushConstant ||
                     var == push_constant_var) {
                     continue;
                 }
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index b79a850..2b84ad1 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -72,7 +72,7 @@
 class Validator {
   public:
     /// The valid type storage layouts typedef
-    using ValidTypeStorageLayouts = std::set<std::pair<const sem::Type*, ast::StorageClass>>;
+    using ValidTypeStorageLayouts = std::set<std::pair<const sem::Type*, ast::AddressSpace>>;
 
     /// Constructor
     /// @param builder the program builder
@@ -389,12 +389,12 @@
 
     /// Validates a variable initializer
     /// @param v the variable to validate
-    /// @param storage_class the storage class of the variable
+    /// @param address_space the address space of the variable
     /// @param storage_type the type of the storage
     /// @param initializer the RHS initializer expression
     /// @returns true on succes, false otherwise
     bool VariableInitializer(const ast::Variable* v,
-                             ast::StorageClass storage_class,
+                             ast::AddressSpace address_space,
                              const sem::Type* storage_type,
                              const sem::Expression* initializer) const;
 
@@ -427,23 +427,23 @@
     /// @returns true on success, false otherwise.
     bool NoDuplicateAttributes(utils::VectorRef<const ast::Attribute*> attributes) const;
 
-    /// Validates a storage class layout
+    /// Validates a address space layout
     /// @param type the type to validate
-    /// @param sc the storage class
+    /// @param sc the address space
     /// @param source the source of the type
     /// @param layouts previously validated storage layouts
     /// @returns true on success, false otherwise
-    bool StorageClassLayout(const sem::Type* type,
-                            ast::StorageClass sc,
+    bool AddressSpaceLayout(const sem::Type* type,
+                            ast::AddressSpace sc,
                             Source source,
                             ValidTypeStorageLayouts& layouts) const;
 
-    /// Validates a storage class layout
+    /// Validates a address space layout
     /// @param var the variable to validate
     /// @param layouts previously validated storage layouts
     /// @param enabled_extensions all the extensions declared in current module
     /// @returns true on success, false otherwise.
-    bool StorageClassLayout(const sem::Variable* var,
+    bool AddressSpaceLayout(const sem::Variable* var,
                             const ast::Extensions& enabled_extensions,
                             ValidTypeStorageLayouts& layouts) const;
 
diff --git a/src/tint/resolver/validator_is_storeable_test.cc b/src/tint/resolver/validator_is_storeable_test.cc
index 015b095..9fa064a 100644
--- a/src/tint/resolver/validator_is_storeable_test.cc
+++ b/src/tint/resolver/validator_is_storeable_test.cc
@@ -78,7 +78,7 @@
 }
 
 TEST_F(ValidatorIsStorableTest, Pointer) {
-    auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::StorageClass::kPrivate,
+    auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::AddressSpace::kPrivate,
                                      ast::Access::kReadWrite);
     EXPECT_FALSE(v()->IsStorable(ptr));
 }
diff --git a/src/tint/resolver/variable_test.cc b/src/tint/resolver/variable_test.cc
index 8842141..e2d3a77 100644
--- a/src/tint/resolver/variable_test.cc
+++ b/src/tint/resolver/variable_test.cc
@@ -238,7 +238,7 @@
     //   var a = a;
     // }
 
-    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* g = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
     auto* v = Var("a", Expr("a"));
     Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(v)});
 
@@ -419,7 +419,7 @@
     auto* b = Let("b", ty.bool_(), b_c);
     auto* s = Let("s", ty.Of(S), s_c);
     auto* a = Let("a", ty.Of(A), a_c);
-    auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
+    auto* p = Let("p", ty.pointer<i32>(ast::AddressSpace::kFunction), p_c);
 
     Func("F", utils::Empty, ty.void_(),
          utils::Vector{
@@ -470,7 +470,7 @@
     // }
     auto* inner = Structure("Inner", utils::Vector{Member("arr", ty.array<i32, 4>())});
     auto* buf = Structure("S", utils::Vector{Member("inner", ty.Of(inner))});
-    auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    auto* storage = GlobalVar("s", ty.Of(buf), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
                               Binding(0_a), Group(0_a));
 
     auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 3_i);
@@ -552,7 +552,7 @@
     //   let a = a;
     // }
 
-    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* g = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
     auto* l = Let("a", Expr("a"));
     Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(l)});
 
@@ -762,7 +762,7 @@
     //   const a = 1i;
     // }
 
-    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* g = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
     auto* c = Const("a", Expr(1_i));
     Func("F", utils::Empty, ty.void_(), utils::Vector{Decl(c)});
 
@@ -1014,9 +1014,8 @@
     EXPECT_EQ(Sem().Get(c)->ConstantValue()->As<i32>(), 42_i);
 }
 
-// Enable when we have @const operators implemented
-TEST_F(ResolverVariableTest, DISABLED_LocalConst_ConstEval) {
-    auto* c = Const("c", Div(Mul(Add(1_i, 2_i), 3_i), 2_i));
+TEST_F(ResolverVariableTest, LocalConst_ConstEval) {
+    auto* c = Const("c", Div(Mul(Add(1_i, 2_i), 3_i), 3_i));
 
     WrapInFunction(c);
 
@@ -1030,16 +1029,16 @@
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Module-scope 'var'
 ////////////////////////////////////////////////////////////////////////////////////////////////////
-TEST_F(ResolverVariableTest, GlobalVar_StorageClass) {
+TEST_F(ResolverVariableTest, GlobalVar_AddressSpace) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
     auto* buf = Structure("S", utils::Vector{Member("m", ty.i32())});
-    auto* private_ = GlobalVar("p", ty.i32(), ast::StorageClass::kPrivate);
-    auto* workgroup = GlobalVar("w", ty.i32(), ast::StorageClass::kWorkgroup);
+    auto* private_ = GlobalVar("p", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* workgroup = GlobalVar("w", ty.i32(), ast::AddressSpace::kWorkgroup);
     auto* uniform =
-        GlobalVar("ub", ty.Of(buf), ast::StorageClass::kUniform, Binding(0_a), Group(0_a));
+        GlobalVar("ub", ty.Of(buf), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
     auto* storage =
-        GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage, Binding(1_a), Group(0_a));
+        GlobalVar("sb", ty.Of(buf), ast::AddressSpace::kStorage, Binding(1_a), Group(0_a));
     auto* handle =
         GlobalVar("h", ty.depth_texture(ast::TextureDimension::k2d), Binding(2_a), Group(0_a));
 
@@ -1058,11 +1057,11 @@
     EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
 }
 
-TEST_F(ResolverVariableTest, GlobalVar_ExplicitStorageClass) {
+TEST_F(ResolverVariableTest, GlobalVar_ExplicitAddressSpace) {
     // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
 
     auto* buf = Structure("S", utils::Vector{Member("m", ty.i32())});
-    auto* storage = GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage,
+    auto* storage = GlobalVar("sb", ty.Of(buf), ast::AddressSpace::kStorage,
                               ast::Access::kReadWrite, Binding(1_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -1182,9 +1181,8 @@
     EXPECT_EQ(Sem().Get(c)->ConstantValue()->As<i32>(), 42_i);
 }
 
-// Enable when we have @const operators implemented
-TEST_F(ResolverVariableTest, DISABLED_GlobalConst_ConstEval) {
-    auto* c = GlobalConst("c", Div(Mul(Add(1_i, 2_i), 3_i), 2_i));
+TEST_F(ResolverVariableTest, GlobalConst_ConstEval) {
+    auto* c = GlobalConst("c", Div(Mul(Add(1_i, 2_i), 3_i), 3_i));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1220,7 +1218,7 @@
     // fn F(a : bool) {
     // }
 
-    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* g = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
     auto* p = Param("a", ty.bool_());
     Func("F", utils::Vector{p}, ty.void_(), utils::Empty);
 
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index ea67071..28f8dc3 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -61,8 +61,8 @@
 TEST_F(ResolverVariableValidationTest, GlobalVarUsedAtModuleScope) {
     // var<private> a : i32;
     // var<private> b : i32 = a;
-    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.i32(), ast::StorageClass::kPrivate, Expr(Source{{56, 78}}, "a"));
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.i32(), ast::AddressSpace::kPrivate, Expr(Source{{56, 78}}, "a"));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), R"(56:78 error: var 'a' cannot be referenced at module-scope
@@ -112,8 +112,8 @@
     // var i : i32;
     // var p : pointer<function, i32> = &v;
     auto* i = Var("i", ty.i32());
-    auto* p = Var("a", ty.pointer<i32>(Source{{56, 78}}, ast::StorageClass::kFunction),
-                  ast::StorageClass::kNone, AddressOf(Source{{12, 34}}, "i"));
+    auto* p = Var("a", ty.pointer<i32>(Source{{56, 78}}, ast::AddressSpace::kFunction),
+                  ast::AddressSpace::kNone, AddressOf(Source{{12, 34}}, "i"));
     WrapInFunction(i, p);
 
     EXPECT_FALSE(r()->Resolve());
@@ -205,7 +205,7 @@
 TEST_F(ResolverVariableValidationTest, LetOfPtrConstructedWithRef) {
     // var a : f32;
     // let b : ptr<function,f32> = a;
-    const auto priv = ast::StorageClass::kFunction;
+    const auto priv = ast::AddressSpace::kFunction;
     auto* var_a = Var("a", ty.f32(), priv);
     auto* var_b = Let(Source{{12, 34}}, "b", ty.pointer<f32>(priv), Expr("a"));
     WrapInFunction(var_a, var_b);
@@ -236,7 +236,7 @@
     //   return 0;
     // }
 
-    GlobalVar("v", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar("v", ty.f32(), ast::AddressSpace::kPrivate, Expr(2.1_f));
 
     WrapInFunction(Var(Source{{12, 34}}, "v", ty.f32(), Expr(2_f)));
 
@@ -295,12 +295,12 @@
                                    Member("inner", ty.Of(inner)),
                                });
     auto* storage =
-        GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, Binding(0_a), Group(0_a));
+        GlobalVar("s", ty.Of(buf), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 2_i);
     auto* ptr =
         Let(Source{{12, 34}}, "p",
-            ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite), AddressOf(expr));
+            ty.pointer<i32>(ast::AddressSpace::kStorage, ast::Access::kReadWrite), AddressOf(expr));
 
     WrapInFunction(ptr);
 
@@ -328,7 +328,7 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 56:78 note: while analysing structure member S.m
 12:34 note: while instantiating 'var' v)");
 }
@@ -357,15 +357,15 @@
     EXPECT_EQ(r()->error(), "12:34 error: function-scope 'var' must have a constructible type");
 }
 
-TEST_F(ResolverVariableValidationTest, InvalidStorageClassForInitializer) {
+TEST_F(ResolverVariableValidationTest, InvalidAddressSpaceForInitializer) {
     // var<workgroup> v : f32 = 1.23;
-    GlobalVar(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kWorkgroup, Expr(1.23_f));
+    GlobalVar(Source{{12, 34}}, "v", ty.f32(), ast::AddressSpace::kWorkgroup, Expr(1.23_f));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: var of storage class 'workgroup' cannot have "
+              "12:34 error: var of address space 'workgroup' cannot have "
               "an initializer. var initializers are only supported for the "
-              "storage classes 'private' and 'function'");
+              "address spacees 'private' and 'function'");
 }
 
 TEST_F(ResolverVariableValidationTest, VectorConstNoType) {
diff --git a/src/tint/sem/function.cc b/src/tint/sem/function.cc
index fc7809b..af2ee12 100644
--- a/src/tint/sem/function.cc
+++ b/src/tint/sem/function.cc
@@ -68,7 +68,7 @@
     VariableBindings ret;
 
     for (auto* global : TransitivelyReferencedGlobals()) {
-        if (global->StorageClass() != ast::StorageClass::kUniform) {
+        if (global->AddressSpace() != ast::AddressSpace::kUniform) {
             continue;
         }
 
@@ -83,7 +83,7 @@
     VariableBindings ret;
 
     for (auto* global : TransitivelyReferencedGlobals()) {
-        if (global->StorageClass() != ast::StorageClass::kStorage) {
+        if (global->AddressSpace() != ast::AddressSpace::kStorage) {
             continue;
         }
 
diff --git a/src/tint/sem/pointer.cc b/src/tint/sem/pointer.cc
index e00a4bf..3918233 100644
--- a/src/tint/sem/pointer.cc
+++ b/src/tint/sem/pointer.cc
@@ -22,19 +22,19 @@
 
 namespace tint::sem {
 
-Pointer::Pointer(const Type* subtype, ast::StorageClass storage_class, ast::Access access)
-    : subtype_(subtype), storage_class_(storage_class), access_(access) {
+Pointer::Pointer(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
+    : subtype_(subtype), address_space_(address_space), access_(access) {
     TINT_ASSERT(Semantic, !subtype->Is<Reference>());
     TINT_ASSERT(Semantic, access != ast::Access::kUndefined);
 }
 
 size_t Pointer::Hash() const {
-    return utils::Hash(TypeInfo::Of<Pointer>().full_hashcode, storage_class_, subtype_, access_);
+    return utils::Hash(TypeInfo::Of<Pointer>().full_hashcode, address_space_, subtype_, access_);
 }
 
 bool Pointer::Equals(const sem::Type& other) const {
     if (auto* o = other.As<Pointer>()) {
-        return o->storage_class_ == storage_class_ && o->subtype_ == subtype_ &&
+        return o->address_space_ == address_space_ && o->subtype_ == subtype_ &&
                o->access_ == access_;
     }
     return false;
@@ -43,8 +43,8 @@
 std::string Pointer::FriendlyName(const SymbolTable& symbols) const {
     std::ostringstream out;
     out << "ptr<";
-    if (storage_class_ != ast::StorageClass::kNone) {
-        out << storage_class_ << ", ";
+    if (address_space_ != ast::AddressSpace::kNone) {
+        out << address_space_ << ", ";
     }
     out << subtype_->FriendlyName(symbols) << ", " << access_;
     out << ">";
diff --git a/src/tint/sem/pointer.h b/src/tint/sem/pointer.h
index 0c82e77..806dcaa 100644
--- a/src/tint/sem/pointer.h
+++ b/src/tint/sem/pointer.h
@@ -18,7 +18,7 @@
 #include <string>
 
 #include "src/tint/ast/access.h"
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/sem/type.h"
 
 namespace tint::sem {
@@ -28,9 +28,9 @@
   public:
     /// Constructor
     /// @param subtype the pointee type
-    /// @param storage_class the storage class of the pointer
+    /// @param address_space the address space of the pointer
     /// @param access the resolved access control of the reference
-    Pointer(const Type* subtype, ast::StorageClass storage_class, ast::Access access);
+    Pointer(const Type* subtype, ast::AddressSpace address_space, ast::Access access);
 
     /// Move constructor
     Pointer(Pointer&&);
@@ -46,8 +46,8 @@
     /// @returns the pointee type
     const Type* StoreType() const { return subtype_; }
 
-    /// @returns the storage class of the pointer
-    ast::StorageClass StorageClass() const { return storage_class_; }
+    /// @returns the address space of the pointer
+    ast::AddressSpace AddressSpace() const { return address_space_; }
 
     /// @returns the access control of the reference
     ast::Access Access() const { return access_; }
@@ -59,7 +59,7 @@
 
   private:
     Type const* const subtype_;
-    ast::StorageClass const storage_class_;
+    ast::AddressSpace const address_space_;
     ast::Access const access_;
 };
 
diff --git a/src/tint/sem/pointer_test.cc b/src/tint/sem/pointer_test.cc
index 575db41..4f7533e 100644
--- a/src/tint/sem/pointer_test.cc
+++ b/src/tint/sem/pointer_test.cc
@@ -21,14 +21,14 @@
 using PointerTest = TestHelper;
 
 TEST_F(PointerTest, Creation) {
-    auto* a = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* b = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* c = create<Pointer>(create<F32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* d = create<Pointer>(create<I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
-    auto* e = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kRead);
+    auto* a = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* b = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* c = create<Pointer>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* d = create<Pointer>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    auto* e = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_TRUE(a->StoreType()->Is<sem::I32>());
-    EXPECT_EQ(a->StorageClass(), ast::StorageClass::kStorage);
+    EXPECT_EQ(a->AddressSpace(), ast::AddressSpace::kStorage);
     EXPECT_EQ(a->Access(), ast::Access::kReadWrite);
 
     EXPECT_EQ(a, b);
@@ -38,11 +38,11 @@
 }
 
 TEST_F(PointerTest, Hash) {
-    auto* a = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* b = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* c = create<Pointer>(create<F32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* d = create<Pointer>(create<I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
-    auto* e = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kRead);
+    auto* a = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* b = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* c = create<Pointer>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* d = create<Pointer>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    auto* e = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_EQ(a->Hash(), b->Hash());
     EXPECT_NE(a->Hash(), c->Hash());
@@ -51,11 +51,11 @@
 }
 
 TEST_F(PointerTest, Equals) {
-    auto* a = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* b = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* c = create<Pointer>(create<F32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
-    auto* d = create<Pointer>(create<I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
-    auto* e = create<Pointer>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kRead);
+    auto* a = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* b = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* c = create<Pointer>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
+    auto* d = create<Pointer>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    auto* e = create<Pointer>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_TRUE(a->Equals(*b));
     EXPECT_FALSE(a->Equals(*c));
@@ -65,12 +65,12 @@
 }
 
 TEST_F(PointerTest, FriendlyName) {
-    auto* r = create<Pointer>(create<I32>(), ast::StorageClass::kNone, ast::Access::kRead);
+    auto* r = create<Pointer>(create<I32>(), ast::AddressSpace::kNone, ast::Access::kRead);
     EXPECT_EQ(r->FriendlyName(Symbols()), "ptr<i32, read>");
 }
 
-TEST_F(PointerTest, FriendlyNameWithStorageClass) {
-    auto* r = create<Pointer>(create<I32>(), ast::StorageClass::kWorkgroup, ast::Access::kRead);
+TEST_F(PointerTest, FriendlyNameWithAddressSpace) {
+    auto* r = create<Pointer>(create<I32>(), ast::AddressSpace::kWorkgroup, ast::Access::kRead);
     EXPECT_EQ(r->FriendlyName(Symbols()), "ptr<workgroup, i32, read>");
 }
 
diff --git a/src/tint/sem/reference.cc b/src/tint/sem/reference.cc
index 4751563..a3a9f24 100644
--- a/src/tint/sem/reference.cc
+++ b/src/tint/sem/reference.cc
@@ -21,19 +21,19 @@
 
 namespace tint::sem {
 
-Reference::Reference(const Type* subtype, ast::StorageClass storage_class, ast::Access access)
-    : subtype_(subtype), storage_class_(storage_class), access_(access) {
+Reference::Reference(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
+    : subtype_(subtype), address_space_(address_space), access_(access) {
     TINT_ASSERT(Semantic, !subtype->Is<Reference>());
     TINT_ASSERT(Semantic, access != ast::Access::kUndefined);
 }
 
 size_t Reference::Hash() const {
-    return utils::Hash(TypeInfo::Of<Reference>().full_hashcode, storage_class_, subtype_, access_);
+    return utils::Hash(TypeInfo::Of<Reference>().full_hashcode, address_space_, subtype_, access_);
 }
 
 bool Reference::Equals(const sem::Type& other) const {
     if (auto* o = other.As<Reference>()) {
-        return o->storage_class_ == storage_class_ && o->subtype_ == subtype_ &&
+        return o->address_space_ == address_space_ && o->subtype_ == subtype_ &&
                o->access_ == access_;
     }
     return false;
@@ -42,8 +42,8 @@
 std::string Reference::FriendlyName(const SymbolTable& symbols) const {
     std::ostringstream out;
     out << "ref<";
-    if (storage_class_ != ast::StorageClass::kNone) {
-        out << storage_class_ << ", ";
+    if (address_space_ != ast::AddressSpace::kNone) {
+        out << address_space_ << ", ";
     }
     out << subtype_->FriendlyName(symbols) << ", " << access_;
     out << ">";
diff --git a/src/tint/sem/reference.h b/src/tint/sem/reference.h
index 5db9b62..f843da6 100644
--- a/src/tint/sem/reference.h
+++ b/src/tint/sem/reference.h
@@ -18,7 +18,7 @@
 #include <string>
 
 #include "src/tint/ast/access.h"
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/sem/type.h"
 
 namespace tint::sem {
@@ -28,9 +28,9 @@
   public:
     /// Constructor
     /// @param subtype the pointee type
-    /// @param storage_class the storage class of the reference
+    /// @param address_space the address space of the reference
     /// @param access the resolved access control of the reference
-    Reference(const Type* subtype, ast::StorageClass storage_class, ast::Access access);
+    Reference(const Type* subtype, ast::AddressSpace address_space, ast::Access access);
 
     /// Move constructor
     Reference(Reference&&);
@@ -46,8 +46,8 @@
     /// @returns the pointee type
     const Type* StoreType() const { return subtype_; }
 
-    /// @returns the storage class of the reference
-    ast::StorageClass StorageClass() const { return storage_class_; }
+    /// @returns the address space of the reference
+    ast::AddressSpace AddressSpace() const { return address_space_; }
 
     /// @returns the resolved access control of the reference.
     ast::Access Access() const { return access_; }
@@ -59,7 +59,7 @@
 
   private:
     Type const* const subtype_;
-    ast::StorageClass const storage_class_;
+    ast::AddressSpace const address_space_;
     ast::Access const access_;
 };
 
diff --git a/src/tint/sem/reference_test.cc b/src/tint/sem/reference_test.cc
index 27b1ebd..53ba21b 100644
--- a/src/tint/sem/reference_test.cc
+++ b/src/tint/sem/reference_test.cc
@@ -22,17 +22,17 @@
 
 TEST_F(ReferenceTest, Creation) {
     auto* a =
-        create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* b =
-        create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* c =
-        create<Reference>(create<F32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* d =
-        create<Reference>(create<I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
-    auto* e = create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kRead);
+        create<Reference>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    auto* e = create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_TRUE(a->StoreType()->Is<sem::I32>());
-    EXPECT_EQ(a->StorageClass(), ast::StorageClass::kStorage);
+    EXPECT_EQ(a->AddressSpace(), ast::AddressSpace::kStorage);
     EXPECT_EQ(a->Access(), ast::Access::kReadWrite);
 
     EXPECT_EQ(a, b);
@@ -43,14 +43,14 @@
 
 TEST_F(ReferenceTest, Hash) {
     auto* a =
-        create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* b =
-        create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* c =
-        create<Reference>(create<F32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* d =
-        create<Reference>(create<I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
-    auto* e = create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kRead);
+        create<Reference>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    auto* e = create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_EQ(a->Hash(), b->Hash());
     EXPECT_NE(a->Hash(), c->Hash());
@@ -60,14 +60,14 @@
 
 TEST_F(ReferenceTest, Equals) {
     auto* a =
-        create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* b =
-        create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* c =
-        create<Reference>(create<F32>(), ast::StorageClass::kStorage, ast::Access::kReadWrite);
+        create<Reference>(create<F32>(), ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto* d =
-        create<Reference>(create<I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
-    auto* e = create<Reference>(create<I32>(), ast::StorageClass::kStorage, ast::Access::kRead);
+        create<Reference>(create<I32>(), ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
+    auto* e = create<Reference>(create<I32>(), ast::AddressSpace::kStorage, ast::Access::kRead);
 
     EXPECT_TRUE(a->Equals(*b));
     EXPECT_FALSE(a->Equals(*c));
@@ -77,12 +77,12 @@
 }
 
 TEST_F(ReferenceTest, FriendlyName) {
-    auto* r = create<Reference>(create<I32>(), ast::StorageClass::kNone, ast::Access::kRead);
+    auto* r = create<Reference>(create<I32>(), ast::AddressSpace::kNone, ast::Access::kRead);
     EXPECT_EQ(r->FriendlyName(Symbols()), "ref<i32, read>");
 }
 
-TEST_F(ReferenceTest, FriendlyNameWithStorageClass) {
-    auto* r = create<Reference>(create<I32>(), ast::StorageClass::kWorkgroup, ast::Access::kRead);
+TEST_F(ReferenceTest, FriendlyNameWithAddressSpace) {
+    auto* r = create<Reference>(create<I32>(), ast::AddressSpace::kWorkgroup, ast::Access::kRead);
     EXPECT_EQ(r->FriendlyName(Symbols()), "ref<workgroup, i32, read>");
 }
 
diff --git a/src/tint/sem/struct.h b/src/tint/sem/struct.h
index 0f3213a..80ab53b 100644
--- a/src/tint/sem/struct.h
+++ b/src/tint/sem/struct.h
@@ -22,7 +22,7 @@
 #include <unordered_set>
 #include <vector>
 
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/ast/struct.h"
 #include "src/tint/sem/node.h"
 #include "src/tint/sem/type.h"
@@ -109,23 +109,23 @@
     /// alignment padding
     uint32_t SizeNoPadding() const { return size_no_padding_; }
 
-    /// Adds the StorageClass usage to the structure.
+    /// Adds the AddressSpace usage to the structure.
     /// @param usage the storage usage
-    void AddUsage(ast::StorageClass usage) { storage_class_usage_.emplace(usage); }
+    void AddUsage(ast::AddressSpace usage) { address_space_usage_.emplace(usage); }
 
-    /// @returns the set of storage class uses of this structure
-    const std::unordered_set<ast::StorageClass>& StorageClassUsage() const {
-        return storage_class_usage_;
+    /// @returns the set of address space uses of this structure
+    const std::unordered_set<ast::AddressSpace>& AddressSpaceUsage() const {
+        return address_space_usage_;
     }
 
-    /// @param usage the ast::StorageClass usage type to query
-    /// @returns true iff this structure has been used as the given storage class
-    bool UsedAs(ast::StorageClass usage) const { return storage_class_usage_.count(usage) > 0; }
+    /// @param usage the ast::AddressSpace usage type to query
+    /// @returns true iff this structure has been used as the given address space
+    bool UsedAs(ast::AddressSpace usage) const { return address_space_usage_.count(usage) > 0; }
 
-    /// @returns true iff this structure has been used by storage class that's
+    /// @returns true iff this structure has been used by address space that's
     /// host-shareable.
     bool IsHostShareable() const {
-        for (auto sc : storage_class_usage_) {
+        for (auto sc : address_space_usage_) {
             if (ast::IsHostShareable(sc)) {
                 return true;
             }
@@ -165,7 +165,7 @@
     const uint32_t align_;
     const uint32_t size_;
     const uint32_t size_no_padding_;
-    std::unordered_set<ast::StorageClass> storage_class_usage_;
+    std::unordered_set<ast::AddressSpace> address_space_usage_;
     std::unordered_set<PipelineStageUsage> pipeline_stage_uses_;
     bool constructible_;
 };
diff --git a/src/tint/sem/type.cc b/src/tint/sem/type.cc
index 23f9164..8ace823 100644
--- a/src/tint/sem/type.cc
+++ b/src/tint/sem/type.cc
@@ -254,9 +254,9 @@
         },
         [&](Default) {
             if (count) {
-                *count = 0;
+                *count = 1;
             }
-            return nullptr;
+            return ty;
         });
 }
 
diff --git a/src/tint/sem/type.h b/src/tint/sem/type.h
index 8bac821..434b913 100644
--- a/src/tint/sem/type.h
+++ b/src/tint/sem/type.h
@@ -140,21 +140,19 @@
     /// @param count if not null, then this is assigned the number of child elements in the type.
     /// For example, the count of an `array<vec3<f32>, 5>` type would be 5.
     /// @returns
-    ///   * `ty` if `ty` is an abstract or scalar
     ///   * the element type if `ty` is a vector or array
     ///   * the column type if `ty` is a matrix
-    ///   * `nullptr` if `ty` is none of the above
+    ///   * `ty` if `ty` is none of the above
     static const Type* ElementOf(const Type* ty, uint32_t* count = nullptr);
 
     /// @param ty the type to obtain the deepest element type from
     /// @param count if not null, then this is assigned the full number of most deeply nested
     /// elements in the type. For example, the count of an `array<vec3<f32>, 5>` type would be 15.
     /// @returns
-    ///   * `ty` if `ty` is an abstract or scalar
     ///   * the element type if `ty` is a vector
     ///   * the matrix element type if `ty` is a matrix
     ///   * the deepest element type if `ty` is an array
-    ///   * `nullptr` if `ty` is none of the above
+    ///   * `ty` if `ty` is none of the above
     static const Type* DeepestElementOf(const Type* ty, uint32_t* count = nullptr);
 
     /// @param types the list of types
diff --git a/src/tint/sem/type_test.cc b/src/tint/sem/type_test.cc
index 5c7e97b..02c76c2 100644
--- a/src/tint/sem/type_test.cc
+++ b/src/tint/sem/type_test.cc
@@ -43,7 +43,7 @@
     const sem::Matrix* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
     const sem::Matrix* mat4x3_af = create<Matrix>(vec3_af, 4u);
     const sem::Reference* ref_u32 =
-        create<Reference>(u32, ast::StorageClass::kPrivate, ast::Access::kReadWrite);
+        create<Reference>(u32, ast::AddressSpace::kPrivate, ast::Access::kReadWrite);
     const sem::Struct* str = create<Struct>(nullptr,
                                             Sym("s"),
                                             StructMemberList{
@@ -192,7 +192,7 @@
     EXPECT_TYPE(Type::ElementOf(mat2x4_f32), vec4_f32);
     EXPECT_TYPE(Type::ElementOf(mat4x2_f32), vec2_f32);
     EXPECT_TYPE(Type::ElementOf(mat4x3_f16), vec3_f16);
-    EXPECT_TYPE(Type::ElementOf(str), nullptr);
+    EXPECT_TYPE(Type::ElementOf(str), str);
     EXPECT_TYPE(Type::ElementOf(arr_i32), i32);
     EXPECT_TYPE(Type::ElementOf(arr_vec3_i32), vec3_i32);
     EXPECT_TYPE(Type::ElementOf(arr_mat4x3_f16), mat4x3_f16);
@@ -237,8 +237,8 @@
     EXPECT_TYPE(Type::ElementOf(mat4x3_f16, &count), vec3_f16);
     EXPECT_EQ(count, 4u);
     count = 42;
-    EXPECT_TYPE(Type::ElementOf(str, &count), nullptr);
-    EXPECT_EQ(count, 0u);
+    EXPECT_TYPE(Type::ElementOf(str, &count), str);
+    EXPECT_EQ(count, 1u);
     count = 42;
     EXPECT_TYPE(Type::ElementOf(arr_i32, &count), i32);
     EXPECT_EQ(count, 5u);
@@ -270,12 +270,12 @@
     EXPECT_TYPE(Type::DeepestElementOf(mat2x4_f32), f32);
     EXPECT_TYPE(Type::DeepestElementOf(mat4x2_f32), f32);
     EXPECT_TYPE(Type::DeepestElementOf(mat4x3_f16), f16);
-    EXPECT_TYPE(Type::DeepestElementOf(str), nullptr);
+    EXPECT_TYPE(Type::DeepestElementOf(str), str);
     EXPECT_TYPE(Type::DeepestElementOf(arr_i32), i32);
     EXPECT_TYPE(Type::DeepestElementOf(arr_vec3_i32), i32);
     EXPECT_TYPE(Type::DeepestElementOf(arr_mat4x3_f16), f16);
     EXPECT_TYPE(Type::DeepestElementOf(arr_mat4x3_af), af);
-    EXPECT_TYPE(Type::DeepestElementOf(arr_str), nullptr);
+    EXPECT_TYPE(Type::DeepestElementOf(arr_str), str);
 
     // With count
     uint32_t count = 42;
@@ -315,8 +315,8 @@
     EXPECT_TYPE(Type::DeepestElementOf(mat4x3_f16, &count), f16);
     EXPECT_EQ(count, 12u);
     count = 42;
-    EXPECT_TYPE(Type::DeepestElementOf(str, &count), nullptr);
-    EXPECT_EQ(count, 0u);
+    EXPECT_TYPE(Type::DeepestElementOf(str, &count), str);
+    EXPECT_EQ(count, 1u);
     count = 42;
     EXPECT_TYPE(Type::DeepestElementOf(arr_i32, &count), i32);
     EXPECT_EQ(count, 5u);
@@ -330,8 +330,8 @@
     EXPECT_TYPE(Type::DeepestElementOf(arr_mat4x3_af, &count), af);
     EXPECT_EQ(count, 60u);
     count = 42;
-    EXPECT_TYPE(Type::DeepestElementOf(arr_str, &count), nullptr);
-    EXPECT_EQ(count, 0u);
+    EXPECT_TYPE(Type::DeepestElementOf(arr_str, &count), str);
+    EXPECT_EQ(count, 5u);
 }
 
 TEST_F(TypeTest, Common2) {
diff --git a/src/tint/sem/variable.cc b/src/tint/sem/variable.cc
index 6dcec63..9336947 100644
--- a/src/tint/sem/variable.cc
+++ b/src/tint/sem/variable.cc
@@ -31,13 +31,13 @@
 Variable::Variable(const ast::Variable* declaration,
                    const sem::Type* type,
                    EvaluationStage stage,
-                   ast::StorageClass storage_class,
+                   ast::AddressSpace address_space,
                    ast::Access access,
                    const Constant* constant_value)
     : declaration_(declaration),
       type_(type),
       stage_(stage),
-      storage_class_(storage_class),
+      address_space_(address_space),
       access_(access),
       constant_value_(constant_value) {}
 
@@ -46,11 +46,11 @@
 LocalVariable::LocalVariable(const ast::Variable* declaration,
                              const sem::Type* type,
                              EvaluationStage stage,
-                             ast::StorageClass storage_class,
+                             ast::AddressSpace address_space,
                              ast::Access access,
                              const sem::Statement* statement,
                              const Constant* constant_value)
-    : Base(declaration, type, stage, storage_class, access, constant_value),
+    : Base(declaration, type, stage, address_space, access, constant_value),
       statement_(statement) {}
 
 LocalVariable::~LocalVariable() = default;
@@ -58,12 +58,12 @@
 GlobalVariable::GlobalVariable(const ast::Variable* declaration,
                                const sem::Type* type,
                                EvaluationStage stage,
-                               ast::StorageClass storage_class,
+                               ast::AddressSpace address_space,
                                ast::Access access,
                                const Constant* constant_value,
                                sem::BindingPoint binding_point,
                                std::optional<uint32_t> location)
-    : Base(declaration, type, stage, storage_class, access, constant_value),
+    : Base(declaration, type, stage, address_space, access, constant_value),
       binding_point_(binding_point),
       location_(location) {}
 
@@ -72,12 +72,12 @@
 Parameter::Parameter(const ast::Parameter* declaration,
                      uint32_t index,
                      const sem::Type* type,
-                     ast::StorageClass storage_class,
+                     ast::AddressSpace address_space,
                      ast::Access access,
                      const ParameterUsage usage /* = ParameterUsage::kNone */,
                      sem::BindingPoint binding_point /* = {} */,
                      std::optional<uint32_t> location /* = std::nullopt */)
-    : Base(declaration, type, EvaluationStage::kRuntime, storage_class, access, nullptr),
+    : Base(declaration, type, EvaluationStage::kRuntime, address_space, access, nullptr),
       index_(index),
       usage_(usage),
       binding_point_(binding_point),
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index 5ea70b9..1d91e15 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -22,7 +22,7 @@
 #include "tint/override_id.h"
 
 #include "src/tint/ast/access.h"
-#include "src/tint/ast/storage_class.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/sem/binding_point.h"
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/parameter_usage.h"
@@ -49,13 +49,13 @@
     /// @param declaration the AST declaration node
     /// @param type the variable type
     /// @param stage the evaluation stage for an expression of this variable type
-    /// @param storage_class the variable storage class
+    /// @param address_space the variable address space
     /// @param access the variable access control type
     /// @param constant_value the constant value for the variable. May be null
     Variable(const ast::Variable* declaration,
              const sem::Type* type,
              EvaluationStage stage,
-             ast::StorageClass storage_class,
+             ast::AddressSpace address_space,
              ast::Access access,
              const Constant* constant_value);
 
@@ -71,8 +71,8 @@
     /// @returns the evaluation stage for an expression of this variable type
     EvaluationStage Stage() const { return stage_; }
 
-    /// @returns the storage class for the variable
-    ast::StorageClass StorageClass() const { return storage_class_; }
+    /// @returns the address space for the variable
+    ast::AddressSpace AddressSpace() const { return address_space_; }
 
     /// @returns the access control for the variable
     ast::Access Access() const { return access_; }
@@ -98,7 +98,7 @@
     const ast::Variable* const declaration_;
     const sem::Type* const type_;
     const EvaluationStage stage_;
-    const ast::StorageClass storage_class_;
+    const ast::AddressSpace address_space_;
     const ast::Access access_;
     const Constant* constant_value_;
     const Expression* constructor_ = nullptr;
@@ -112,14 +112,14 @@
     /// @param declaration the AST declaration node
     /// @param type the variable type
     /// @param stage the evaluation stage for an expression of this variable type
-    /// @param storage_class the variable storage class
+    /// @param address_space the variable address space
     /// @param access the variable access control type
     /// @param statement the statement that declared this local variable
     /// @param constant_value the constant value for the variable. May be null
     LocalVariable(const ast::Variable* declaration,
                   const sem::Type* type,
                   EvaluationStage stage,
-                  ast::StorageClass storage_class,
+                  ast::AddressSpace address_space,
                   ast::Access access,
                   const sem::Statement* statement,
                   const Constant* constant_value);
@@ -149,7 +149,7 @@
     /// @param declaration the AST declaration node
     /// @param type the variable type
     /// @param stage the evaluation stage for an expression of this variable type
-    /// @param storage_class the variable storage class
+    /// @param address_space the variable address space
     /// @param access the variable access control type
     /// @param constant_value the constant value for the variable. May be null
     /// @param binding_point the optional resource binding point of the variable
@@ -160,7 +160,7 @@
     GlobalVariable(const ast::Variable* declaration,
                    const sem::Type* type,
                    EvaluationStage stage,
-                   ast::StorageClass storage_class,
+                   ast::AddressSpace address_space,
                    ast::Access access,
                    const Constant* constant_value,
                    sem::BindingPoint binding_point = {},
@@ -195,7 +195,7 @@
     /// @param declaration the AST declaration node
     /// @param index the index of the parmeter in the function
     /// @param type the variable type
-    /// @param storage_class the variable storage class
+    /// @param address_space the variable address space
     /// @param access the variable access control type
     /// @param usage the semantic usage for the parameter
     /// @param binding_point the optional resource binding point of the parameter
@@ -203,7 +203,7 @@
     Parameter(const ast::Parameter* declaration,
               uint32_t index,
               const sem::Type* type,
-              ast::StorageClass storage_class,
+              ast::AddressSpace address_space,
               ast::Access access,
               const ParameterUsage usage = ParameterUsage::kNone,
               sem::BindingPoint binding_point = {},
diff --git a/src/tint/traits.h b/src/tint/traits.h
index d830d2c..8dcc65b 100644
--- a/src/tint/traits.h
+++ b/src/tint/traits.h
@@ -73,15 +73,24 @@
 
 /// SignatureOfT is an alias to `typename SignatureOf<F>::type`.
 template <typename F>
-using SignatureOfT = typename SignatureOf<F>::type;
+using SignatureOfT = typename SignatureOf<Decay<F>>::type;
 
 /// ParameterType is an alias to `typename SignatureOf<F>::type::parameter<N>`.
 template <typename F, std::size_t N>
-using ParameterType = typename SignatureOfT<F>::template parameter<N>;
+using ParameterType = typename SignatureOfT<Decay<F>>::template parameter<N>;
+
+/// LastParameterType returns the type of the last parameter of `F`. `F` must have at least one
+/// parameter.
+template <typename F>
+using LastParameterType = ParameterType<F, SignatureOfT<Decay<F>>::parameter_count - 1>;
 
 /// ReturnType is an alias to `typename SignatureOf<F>::type::ret`.
 template <typename F>
-using ReturnType = typename SignatureOfT<F>::ret;
+using ReturnType = typename SignatureOfT<Decay<F>>::ret;
+
+/// Returns true iff decayed T and decayed U are the same.
+template <typename T, typename U>
+static constexpr bool IsType = std::is_same<Decay<T>, Decay<U>>::value;
 
 /// IsTypeOrDerived<T, BASE> is true iff `T` is of type `BASE`, or derives from
 /// `BASE`.
diff --git a/src/tint/transform/add_block_attribute.cc b/src/tint/transform/add_block_attribute.cc
index 619083d..97c531e 100644
--- a/src/tint/transform/add_block_attribute.cc
+++ b/src/tint/transform/add_block_attribute.cc
@@ -29,7 +29,7 @@
 
 namespace {
 
-bool IsUsedAsNonBuffer(const std::unordered_set<tint::ast::StorageClass>& uses) {
+bool IsUsedAsNonBuffer(const std::unordered_set<tint::ast::AddressSpace>& uses) {
     for (auto use : uses) {
         if (!ast::IsHostShareable(use)) {
             return true;
@@ -73,8 +73,8 @@
     // Process global 'var' declarations that are buffers.
     for (auto* global : ctx.src->AST().GlobalVariables()) {
         auto* var = sem.Get(global);
-        if (!ast::IsHostShareable(var->StorageClass())) {
-            // Not declared in a host-sharable storage class
+        if (!ast::IsHostShareable(var->AddressSpace())) {
+            // Not declared in a host-sharable address space
             continue;
         }
 
@@ -83,7 +83,7 @@
         bool needs_wrapping =
             !str ||                                       // Type is not a structure
             nested_structs.Contains(str) ||               // Structure is nested by another type
-            IsUsedAsNonBuffer(str->StorageClassUsage());  // Structure is used as a non-buffer usage
+            IsUsedAsNonBuffer(str->AddressSpaceUsage());  // Structure is used as a non-buffer usage
 
         if (needs_wrapping) {
             const char* kMemberName = "inner";
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index 71a5cca..3938b0c 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -162,7 +162,7 @@
                                                       u32((max_buffer_size_index / 4) + 1))),
                 });
             buffer_size_ubo = ctx.dst->GlobalVar(ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct),
-                                                 ast::StorageClass::kUniform,
+                                                 ast::AddressSpace::kUniform,
                                                  ctx.dst->Group(AInt(cfg->ubo_binding.group)),
                                                  ctx.dst->Binding(AInt(cfg->ubo_binding.binding)));
         }
diff --git a/src/tint/transform/binding_remapper.cc b/src/tint/transform/binding_remapper.cc
index 487752e..a9a3dfb 100644
--- a/src/tint/transform/binding_remapper.cc
+++ b/src/tint/transform/binding_remapper.cc
@@ -128,18 +128,18 @@
                     return;
                 }
                 auto* sem = ctx.src->Sem().Get(var);
-                if (sem->StorageClass() != ast::StorageClass::kStorage) {
+                if (sem->AddressSpace() != ast::AddressSpace::kStorage) {
                     ctx.dst->Diagnostics().add_error(
                         diag::System::Transform,
-                        "cannot apply access control to variable with storage class " +
-                            std::string(utils::ToString(sem->StorageClass())));
+                        "cannot apply access control to variable with address space " +
+                            std::string(utils::ToString(sem->AddressSpace())));
                     return;
                 }
                 auto* ty = sem->Type()->UnwrapRef();
                 const ast::Type* inner_ty = CreateASTTypeFor(ctx, ty);
                 auto* new_var =
                     ctx.dst->Var(ctx.Clone(var->source), ctx.Clone(var->symbol), inner_ty,
-                                 var->declared_storage_class, ac, ctx.Clone(var->constructor),
+                                 var->declared_address_space, ac, ctx.Clone(var->constructor),
                                  ctx.Clone(var->attributes));
                 ctx.Replace(var, new_var);
             }
diff --git a/src/tint/transform/calculate_array_length.cc b/src/tint/transform/calculate_array_length.cc
index b06f7b6..2ca5e54 100644
--- a/src/tint/transform/calculate_array_length.cc
+++ b/src/tint/transform/calculate_array_length.cc
@@ -103,11 +103,11 @@
                 name,
                 utils::Vector{
                     ctx.dst->Param("buffer",
-                                   ctx.dst->ty.pointer(type, buffer_type->StorageClass(),
+                                   ctx.dst->ty.pointer(type, buffer_type->AddressSpace(),
                                                        buffer_type->Access()),
                                    utils::Vector{disable_validation}),
                     ctx.dst->Param("result", ctx.dst->ty.pointer(ctx.dst->ty.u32(),
-                                                                 ast::StorageClass::kFunction)),
+                                                                 ast::AddressSpace::kFunction)),
                 },
                 ctx.dst->ty.void_(), nullptr,
                 utils::Vector{
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index b08d44d..0ff1084 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -190,15 +190,15 @@
                                                      ast::InterpolationSampling::kNone));
             }
 
-            // Disable validation for use of the `input` storage class.
-            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
+            // Disable validation for use of the `input` address space.
+            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
 
             // In GLSL, if it's a builtin, override the name with the
             // corresponding gl_ builtin name
             auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
             if (cfg.shader_style == ShaderStyle::kGlsl && builtin) {
                 name = GLSLBuiltinToString(builtin->builtin, func_ast->PipelineStage(),
-                                           ast::StorageClass::kIn);
+                                           ast::AddressSpace::kIn);
             }
             auto symbol = ctx.dst->Symbols().New(name);
 
@@ -215,7 +215,7 @@
                     value = ctx.dst->IndexAccessor(value, 0_i);
                 }
             }
-            ctx.dst->GlobalVar(symbol, ast_type, ast::StorageClass::kIn, std::move(attributes));
+            ctx.dst->GlobalVar(symbol, ast_type, ast::AddressSpace::kIn, std::move(attributes));
             return value;
         } else if (cfg.shader_style == ShaderStyle::kMsl &&
                    ast::HasAttribute<ast::BuiltinAttribute>(attributes)) {
@@ -264,7 +264,7 @@
         if (cfg.shader_style == ShaderStyle::kGlsl) {
             if (auto* b = ast::GetAttribute<ast::BuiltinAttribute>(attributes)) {
                 name = GLSLBuiltinToString(b->builtin, func_ast->PipelineStage(),
-                                           ast::StorageClass::kOut);
+                                           ast::AddressSpace::kOut);
                 value = ToGLSLBuiltin(b->builtin, value, type);
             }
         }
@@ -480,9 +480,9 @@
     /// Create and assign the wrapper function's output variables.
     void CreateGlobalOutputVariables() {
         for (auto& outval : wrapper_output_values) {
-            // Disable validation for use of the `output` storage class.
+            // Disable validation for use of the `output` address space.
             utils::Vector<const ast::Attribute*, 8> attributes = std::move(outval.attributes);
-            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
+            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
 
             // Create the global variable and assign it the output value.
             auto name = ctx.dst->Symbols().New(outval.name);
@@ -494,7 +494,7 @@
                 type = ctx.dst->ty.array(type, 1_u);
                 lhs = ctx.dst->IndexAccessor(lhs, 0_i);
             }
-            ctx.dst->GlobalVar(name, type, ast::StorageClass::kOut, std::move(attributes));
+            ctx.dst->GlobalVar(name, type, ast::AddressSpace::kOut, std::move(attributes));
             wrapper_body.Push(ctx.dst->Assign(lhs, outval.value));
         }
     }
@@ -634,11 +634,11 @@
     /// Retrieve the gl_ string corresponding to a builtin.
     /// @param builtin the builtin
     /// @param stage the current pipeline stage
-    /// @param storage_class the storage class (input or output)
+    /// @param address_space the address space (input or output)
     /// @returns the gl_ string corresponding to that builtin
     const char* GLSLBuiltinToString(ast::BuiltinValue builtin,
                                     ast::PipelineStage stage,
-                                    ast::StorageClass storage_class) {
+                                    ast::AddressSpace address_space) {
         switch (builtin) {
             case ast::BuiltinValue::kPosition:
                 switch (stage) {
@@ -670,7 +670,7 @@
             case ast::BuiltinValue::kSampleIndex:
                 return "gl_SampleID";
             case ast::BuiltinValue::kSampleMask:
-                if (storage_class == ast::StorageClass::kIn) {
+                if (address_space == ast::AddressSpace::kIn) {
                     return "gl_SampleMaskIn";
                 } else {
                     return "gl_SampleMask";
diff --git a/src/tint/transform/canonicalize_entry_point_io_test.cc b/src/tint/transform/canonicalize_entry_point_io_test.cc
index f2af213..3fc0033 100644
--- a/src/tint/transform/canonicalize_entry_point_io_test.cc
+++ b/src/tint/transform/canonicalize_entry_point_io_test.cc
@@ -67,11 +67,11 @@
 )";
 
     auto* expect = R"(
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> loc1_1 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> loc1_1 : f32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> loc2_1 : vec4<u32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> loc2_1 : vec4<u32>;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<in> coord_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<in> coord_1 : vec4<f32>;
 
 fn frag_main_inner(loc1 : f32, loc2 : vec4<u32>, coord : vec4<f32>) {
   var col : f32 = (coord.x * loc1);
@@ -251,13 +251,13 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> loc0_1 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> loc0_1 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> loc1_1 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> loc1_1 : f32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> loc2_1 : vec4<u32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> loc2_1 : vec4<u32>;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<in> coord_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<in> coord_1 : vec4<f32>;
 
 struct FragBuiltins {
   coord : vec4<f32>,
@@ -304,13 +304,13 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> loc0_1 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> loc0_1 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> loc1_1 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> loc1_1 : f32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> loc2_1 : vec4<u32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> loc2_1 : vec4<u32>;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<in> coord_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<in> coord_1 : vec4<f32>;
 
 fn frag_main_inner(loc0 : f32, locations : FragLocations, builtins : FragBuiltins) {
   var col : f32 = ((builtins.coord.x * locations.loc1) + loc0);
@@ -567,7 +567,7 @@
 )";
 
     auto* expect = R"(
-@builtin(frag_depth) @internal(disable_validation__ignore_storage_class) var<out> value : f32;
+@builtin(frag_depth) @internal(disable_validation__ignore_address_space) var<out> value : f32;
 
 fn frag_main_inner() -> f32 {
   return 1.0;
@@ -674,11 +674,11 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<out> color_1 : vec4<f32>;
+@location(0) @internal(disable_validation__ignore_address_space) var<out> color_1 : vec4<f32>;
 
-@builtin(frag_depth) @internal(disable_validation__ignore_storage_class) var<out> depth_1 : f32;
+@builtin(frag_depth) @internal(disable_validation__ignore_address_space) var<out> depth_1 : f32;
 
-@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<out> mask_1 : array<u32, 1u>;
+@builtin(sample_mask) @internal(disable_validation__ignore_address_space) var<out> mask_1 : array<u32, 1u>;
 
 struct FragOutput {
   color : vec4<f32>,
@@ -729,11 +729,11 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<out> color_1 : vec4<f32>;
+@location(0) @internal(disable_validation__ignore_address_space) var<out> color_1 : vec4<f32>;
 
-@builtin(frag_depth) @internal(disable_validation__ignore_storage_class) var<out> depth_1 : f32;
+@builtin(frag_depth) @internal(disable_validation__ignore_address_space) var<out> depth_1 : f32;
 
-@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<out> mask_1 : array<u32, 1u>;
+@builtin(sample_mask) @internal(disable_validation__ignore_address_space) var<out> mask_1 : array<u32, 1u>;
 
 fn frag_main_inner() -> FragOutput {
   var output : FragOutput;
@@ -1028,13 +1028,13 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> value_1 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> value_1 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> mul_1 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> mul_1 : f32;
 
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> value_2 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> value_2 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> mul_2 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> mul_2 : f32;
 
 struct FragmentInput {
   value : f32,
@@ -1094,13 +1094,13 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> value_1 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> value_1 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> mul_1 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> mul_1 : f32;
 
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> value_2 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> value_2 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> mul_2 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> mul_2 : f32;
 
 fn frag_main1_inner(inputs : FragmentInput) {
   var x : f32 = foo(inputs);
@@ -1952,39 +1952,39 @@
 
     auto* expect =
         R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> i_1 : i32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> i_1 : i32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> u_1 : u32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> u_1 : u32;
 
-@location(2) @internal(disable_validation__ignore_storage_class) var<in> vi_1 : vec4<i32>;
+@location(2) @internal(disable_validation__ignore_address_space) var<in> vi_1 : vec4<i32>;
 
-@location(3) @internal(disable_validation__ignore_storage_class) var<in> vu_1 : vec4<u32>;
+@location(3) @internal(disable_validation__ignore_address_space) var<in> vu_1 : vec4<u32>;
 
-@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_2 : i32;
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> i_2 : i32;
 
-@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_2 : u32;
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> u_2 : u32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_2 : vec4<i32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> vi_2 : vec4<i32>;
 
-@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_2 : vec4<u32>;
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> vu_2 : vec4<u32>;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> pos_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> pos_1 : vec4<f32>;
 
-@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> i_3 : i32;
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> i_3 : i32;
 
-@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> u_3 : u32;
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> u_3 : u32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vi_3 : vec4<i32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> vi_3 : vec4<i32>;
 
-@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>;
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> vu_3 : vec4<u32>;
 
-@location(0) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
+@location(0) @internal(disable_validation__ignore_address_space) var<out> i_4 : i32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
+@location(1) @internal(disable_validation__ignore_address_space) var<out> u_4 : u32;
 
-@location(2) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
+@location(2) @internal(disable_validation__ignore_address_space) var<out> vi_4 : vec4<i32>;
 
-@location(3) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
+@location(3) @internal(disable_validation__ignore_address_space) var<out> vu_4 : vec4<u32>;
 
 struct VertexIn {
   i : i32,
@@ -2082,39 +2082,39 @@
 
     auto* expect =
         R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> i_1 : i32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> i_1 : i32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> u_1 : u32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> u_1 : u32;
 
-@location(2) @internal(disable_validation__ignore_storage_class) var<in> vi_1 : vec4<i32>;
+@location(2) @internal(disable_validation__ignore_address_space) var<in> vi_1 : vec4<i32>;
 
-@location(3) @internal(disable_validation__ignore_storage_class) var<in> vu_1 : vec4<u32>;
+@location(3) @internal(disable_validation__ignore_address_space) var<in> vu_1 : vec4<u32>;
 
-@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> i_2 : i32;
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> i_2 : i32;
 
-@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> u_2 : u32;
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> u_2 : u32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vi_2 : vec4<i32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> vi_2 : vec4<i32>;
 
-@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<out> vu_2 : vec4<u32>;
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<out> vu_2 : vec4<u32>;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> pos_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> pos_1 : vec4<f32>;
 
-@location(0) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> i_3 : i32;
+@location(0) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> i_3 : i32;
 
-@location(1) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> u_3 : u32;
+@location(1) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> u_3 : u32;
 
-@location(2) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vi_3 : vec4<i32>;
+@location(2) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> vi_3 : vec4<i32>;
 
-@location(3) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> vu_3 : vec4<u32>;
+@location(3) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> vu_3 : vec4<u32>;
 
-@location(0) @internal(disable_validation__ignore_storage_class) var<out> i_4 : i32;
+@location(0) @internal(disable_validation__ignore_address_space) var<out> i_4 : i32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<out> u_4 : u32;
+@location(1) @internal(disable_validation__ignore_address_space) var<out> u_4 : u32;
 
-@location(2) @internal(disable_validation__ignore_storage_class) var<out> vi_4 : vec4<i32>;
+@location(2) @internal(disable_validation__ignore_address_space) var<out> vi_4 : vec4<i32>;
 
-@location(3) @internal(disable_validation__ignore_storage_class) var<out> vu_4 : vec4<u32>;
+@location(3) @internal(disable_validation__ignore_address_space) var<out> vu_4 : vec4<u32>;
 
 fn vert_main_inner(in : VertexIn) -> VertexOut {
   return VertexOut(in.i, in.u, in.vi, in.vu, vec4<f32>());
@@ -3161,9 +3161,9 @@
 )";
 
     auto* expect = R"(
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> value : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> value : vec4<f32>;
 
-@builtin(point_size) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size : f32;
+@builtin(point_size) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size : f32;
 
 fn vert_main_inner() -> vec4<f32> {
   return vec4<f32>();
@@ -3236,9 +3236,9 @@
 )";
 
     auto* expect = R"(
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> pos_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> pos_1 : vec4<f32>;
 
-@builtin(point_size) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size : f32;
+@builtin(point_size) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size : f32;
 
 struct VertOut {
   pos : vec4<f32>,
@@ -3277,9 +3277,9 @@
 )";
 
     auto* expect = R"(
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> pos_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> pos_1 : vec4<f32>;
 
-@builtin(point_size) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size : f32;
+@builtin(point_size) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size : f32;
 
 fn vert_main_inner() -> VertOut {
   return VertOut();
@@ -3424,15 +3424,15 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> collide_2 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> collide_2 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> collide_3 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> collide_3 : f32;
 
-@location(0) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_3 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size_3 : f32;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_1_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size_1_1 : vec4<f32>;
 
-@builtin(point_size) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_4 : f32;
+@builtin(point_size) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size_4 : f32;
 
 var<private> vertex_point_size : f32;
 
@@ -3502,15 +3502,15 @@
 )";
 
     auto* expect = R"(
-@location(0) @internal(disable_validation__ignore_storage_class) var<in> collide_2 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<in> collide_2 : f32;
 
-@location(1) @internal(disable_validation__ignore_storage_class) var<in> collide_3 : f32;
+@location(1) @internal(disable_validation__ignore_address_space) var<in> collide_3 : f32;
 
-@location(0) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_3 : f32;
+@location(0) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size_3 : f32;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_1_1 : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size_1_1 : vec4<f32>;
 
-@builtin(point_size) @internal(disable_validation__ignore_storage_class) var<out> vertex_point_size_4 : f32;
+@builtin(point_size) @internal(disable_validation__ignore_address_space) var<out> vertex_point_size_4 : f32;
 
 fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
   let x = (collide.collide + collide_1.collide);
@@ -3868,11 +3868,11 @@
 )";
 
     auto* expect = R"(
-@builtin(sample_index) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> sample_index_1 : u32;
+@builtin(sample_index) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> sample_index_1 : u32;
 
-@builtin(sample_mask) @interpolate(flat) @internal(disable_validation__ignore_storage_class) var<in> mask_in_1 : array<u32, 1u>;
+@builtin(sample_mask) @interpolate(flat) @internal(disable_validation__ignore_address_space) var<in> mask_in_1 : array<u32, 1u>;
 
-@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<out> value : array<u32, 1u>;
+@builtin(sample_mask) @internal(disable_validation__ignore_address_space) var<out> value : array<u32, 1u>;
 
 fn main_inner(sample_index : u32, mask_in : u32) -> u32 {
   return mask_in;
@@ -3903,11 +3903,11 @@
 )";
 
     auto* expect = R"(
-@builtin(sample_index) @internal(disable_validation__ignore_storage_class) var<in> gl_SampleID : i32;
+@builtin(sample_index) @internal(disable_validation__ignore_address_space) var<in> gl_SampleID : i32;
 
-@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<in> gl_SampleMaskIn : array<i32, 1u>;
+@builtin(sample_mask) @internal(disable_validation__ignore_address_space) var<in> gl_SampleMaskIn : array<i32, 1u>;
 
-@builtin(sample_mask) @internal(disable_validation__ignore_storage_class) var<out> gl_SampleMask : array<i32, 1u>;
+@builtin(sample_mask) @internal(disable_validation__ignore_address_space) var<out> gl_SampleMask : array<i32, 1u>;
 
 fn fragment_main(sample_index : u32, mask_in : u32) -> u32 {
   return mask_in;
@@ -3938,11 +3938,11 @@
 )";
 
     auto* expect = R"(
-@builtin(vertex_index) @internal(disable_validation__ignore_storage_class) var<in> gl_VertexID : i32;
+@builtin(vertex_index) @internal(disable_validation__ignore_address_space) var<in> gl_VertexID : i32;
 
-@builtin(instance_index) @internal(disable_validation__ignore_storage_class) var<in> gl_InstanceID : i32;
+@builtin(instance_index) @internal(disable_validation__ignore_address_space) var<in> gl_InstanceID : i32;
 
-@builtin(position) @internal(disable_validation__ignore_storage_class) var<out> gl_Position : vec4<f32>;
+@builtin(position) @internal(disable_validation__ignore_address_space) var<out> gl_Position : vec4<f32>;
 
 fn vertex_main(vertexID : u32, instanceID : u32) -> vec4<f32> {
   return vec4<f32>((f32(vertexID) + f32(instanceID)));
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 5920d5b..ffdf88e 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -97,17 +97,17 @@
 
 /// LoadStoreKey is the unordered map key to a load or store intrinsic.
 struct LoadStoreKey {
-    ast::StorageClass const storage_class;  // buffer storage class
+    ast::AddressSpace const address_space;  // buffer address space
     ast::Access const access;               // buffer access
     sem::Type const* buf_ty = nullptr;      // buffer type
     sem::Type const* el_ty = nullptr;       // element type
     bool operator==(const LoadStoreKey& rhs) const {
-        return storage_class == rhs.storage_class && access == rhs.access && buf_ty == rhs.buf_ty &&
+        return address_space == rhs.address_space && access == rhs.access && buf_ty == rhs.buf_ty &&
                el_ty == rhs.el_ty;
     }
     struct Hasher {
         inline std::size_t operator()(const LoadStoreKey& u) const {
-            return utils::Hash(u.storage_class, u.access, u.buf_ty, u.el_ty);
+            return utils::Hash(u.address_space, u.access, u.buf_ty, u.el_ty);
         }
     };
 };
@@ -195,7 +195,7 @@
 /// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied
 /// to a stub function to load the type `ty`.
 DecomposeMemoryAccess::Intrinsic* IntrinsicLoadFor(ProgramBuilder* builder,
-                                                   ast::StorageClass storage_class,
+                                                   ast::AddressSpace address_space,
                                                    const sem::Type* ty) {
     DecomposeMemoryAccess::Intrinsic::DataType type;
     if (!IntrinsicDataTypeFor(ty, type)) {
@@ -203,13 +203,13 @@
     }
     return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
         builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kLoad,
-        storage_class, type);
+        address_space, type);
 }
 
 /// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied
 /// to a stub function to store the type `ty`.
 DecomposeMemoryAccess::Intrinsic* IntrinsicStoreFor(ProgramBuilder* builder,
-                                                    ast::StorageClass storage_class,
+                                                    ast::AddressSpace address_space,
                                                     const sem::Type* ty) {
     DecomposeMemoryAccess::Intrinsic::DataType type;
     if (!IntrinsicDataTypeFor(ty, type)) {
@@ -217,7 +217,7 @@
     }
     return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
         builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kStore,
-        storage_class, type);
+        address_space, type);
 }
 
 /// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied
@@ -272,7 +272,7 @@
         return nullptr;
     }
     return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
-        builder->ID(), builder->AllocateNodeID(), op, ast::StorageClass::kStorage, type);
+        builder->ID(), builder->AllocateNodeID(), op, ast::AddressSpace::kStorage, type);
 }
 
 /// BufferAccess describes a single storage or uniform buffer access
@@ -436,20 +436,20 @@
     Symbol LoadFunc(const sem::Type* buf_ty,
                     const sem::Type* el_ty,
                     const sem::VariableUser* var_user) {
-        auto storage_class = var_user->Variable()->StorageClass();
+        auto address_space = var_user->Variable()->AddressSpace();
         auto access = var_user->Variable()->Access();
         return utils::GetOrCreate(
-            load_funcs, LoadStoreKey{storage_class, access, buf_ty, el_ty}, [&] {
+            load_funcs, LoadStoreKey{address_space, access, buf_ty, el_ty}, [&] {
                 utils::Vector params{
                     b.Param("buffer",
-                            b.ty.pointer(CreateASTTypeFor(ctx, buf_ty), storage_class, access),
+                            b.ty.pointer(CreateASTTypeFor(ctx, buf_ty), address_space, access),
                             utils::Vector{b.Disable(ast::DisabledValidation::kFunctionParameter)}),
                     b.Param("offset", b.ty.u32()),
                 };
 
                 auto name = b.Sym();
 
-                if (auto* intrinsic = IntrinsicLoadFor(ctx.dst, storage_class, el_ty)) {
+                if (auto* intrinsic = IntrinsicLoadFor(ctx.dst, address_space, el_ty)) {
                     auto* el_ast_ty = CreateASTTypeFor(ctx, el_ty);
                     auto* func = b.create<ast::Function>(
                         name, params, el_ast_ty, nullptr,
@@ -532,13 +532,13 @@
     Symbol StoreFunc(const sem::Type* buf_ty,
                      const sem::Type* el_ty,
                      const sem::VariableUser* var_user) {
-        auto storage_class = var_user->Variable()->StorageClass();
+        auto address_space = var_user->Variable()->AddressSpace();
         auto access = var_user->Variable()->Access();
         return utils::GetOrCreate(
-            store_funcs, LoadStoreKey{storage_class, access, buf_ty, el_ty}, [&] {
+            store_funcs, LoadStoreKey{address_space, access, buf_ty, el_ty}, [&] {
                 utils::Vector params{
                     b.Param("buffer",
-                            b.ty.pointer(CreateASTTypeFor(ctx, buf_ty), storage_class, access),
+                            b.ty.pointer(CreateASTTypeFor(ctx, buf_ty), address_space, access),
                             utils::Vector{b.Disable(ast::DisabledValidation::kFunctionParameter)}),
                     b.Param("offset", b.ty.u32()),
                     b.Param("value", CreateASTTypeFor(ctx, el_ty)),
@@ -546,7 +546,7 @@
 
                 auto name = b.Sym();
 
-                if (auto* intrinsic = IntrinsicStoreFor(ctx.dst, storage_class, el_ty)) {
+                if (auto* intrinsic = IntrinsicStoreFor(ctx.dst, address_space, el_ty)) {
                     auto* func = b.create<ast::Function>(
                         name, params, b.ty.void_(), nullptr,
                         utils::Vector{
@@ -648,7 +648,7 @@
             // atomic. This is replaced with two parameters: the buffer and offset.
             utils::Vector params{
                 b.Param("buffer",
-                        b.ty.pointer(CreateASTTypeFor(ctx, buf_ty), ast::StorageClass::kStorage,
+                        b.ty.pointer(CreateASTTypeFor(ctx, buf_ty), ast::AddressSpace::kStorage,
                                      access),
                         utils::Vector{b.Disable(ast::DisabledValidation::kFunctionParameter)}),
                 b.Param("offset", b.ty.u32()),
@@ -706,9 +706,9 @@
 DecomposeMemoryAccess::Intrinsic::Intrinsic(ProgramID pid,
                                             ast::NodeID nid,
                                             Op o,
-                                            ast::StorageClass sc,
+                                            ast::AddressSpace sc,
                                             DataType ty)
-    : Base(pid, nid), op(o), storage_class(sc), type(ty) {}
+    : Base(pid, nid), op(o), address_space(sc), type(ty) {}
 DecomposeMemoryAccess::Intrinsic::~Intrinsic() = default;
 std::string DecomposeMemoryAccess::Intrinsic::InternalName() const {
     std::stringstream ss;
@@ -753,7 +753,7 @@
             ss << "intrinsic_atomic_compare_exchange_weak_";
             break;
     }
-    ss << storage_class << "_";
+    ss << address_space << "_";
     switch (type) {
         case DataType::kU32:
             ss << "u32";
@@ -798,7 +798,7 @@
 const DecomposeMemoryAccess::Intrinsic* DecomposeMemoryAccess::Intrinsic::Clone(
     CloneContext* ctx) const {
     return ctx->dst->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
-        ctx->dst->ID(), ctx->dst->AllocateNodeID(), op, storage_class, type);
+        ctx->dst->ID(), ctx->dst->AllocateNodeID(), op, address_space, type);
 }
 
 bool DecomposeMemoryAccess::Intrinsic::IsAtomic() const {
@@ -811,8 +811,8 @@
 bool DecomposeMemoryAccess::ShouldRun(const Program* program, const DataMap&) const {
     for (auto* decl : program->AST().GlobalDeclarations()) {
         if (auto* var = program->Sem().Get<sem::Variable>(decl)) {
-            if (var->StorageClass() == ast::StorageClass::kStorage ||
-                var->StorageClass() == ast::StorageClass::kUniform) {
+            if (var->AddressSpace() == ast::AddressSpace::kStorage ||
+                var->AddressSpace() == ast::AddressSpace::kUniform) {
                 return true;
             }
         }
@@ -837,8 +837,8 @@
         if (auto* ident = node->As<ast::IdentifierExpression>()) {
             // X
             if (auto* var = sem.Get<sem::VariableUser>(ident)) {
-                if (var->Variable()->StorageClass() == ast::StorageClass::kStorage ||
-                    var->Variable()->StorageClass() == ast::StorageClass::kUniform) {
+                if (var->Variable()->AddressSpace() == ast::AddressSpace::kStorage ||
+                    var->Variable()->AddressSpace() == ast::AddressSpace::kUniform) {
                     // Variable to a storage or uniform buffer
                     state.AddAccess(ident, {
                                                var,
diff --git a/src/tint/transform/decompose_memory_access.h b/src/tint/transform/decompose_memory_access.h
index 1d95dc4..2e92a3a 100644
--- a/src/tint/transform/decompose_memory_access.h
+++ b/src/tint/transform/decompose_memory_access.h
@@ -75,9 +75,9 @@
         /// @param pid the identifier of the program that owns this node
         /// @param nid the unique node identifier
         /// @param o the op of the intrinsic
-        /// @param sc the storage class of the buffer
+        /// @param sc the address space of the buffer
         /// @param ty the data type of the intrinsic
-        Intrinsic(ProgramID pid, ast::NodeID nid, Op o, ast::StorageClass sc, DataType ty);
+        Intrinsic(ProgramID pid, ast::NodeID nid, Op o, ast::AddressSpace sc, DataType ty);
         /// Destructor
         ~Intrinsic() override;
 
@@ -96,8 +96,8 @@
         /// The op of the intrinsic
         const Op op;
 
-        /// The storage class of the buffer this intrinsic operates on
-        ast::StorageClass const storage_class;
+        /// The address space of the buffer this intrinsic operates on
+        ast::AddressSpace const address_space;
 
         /// The type of the intrinsic
         const DataType type;
diff --git a/src/tint/transform/decompose_strided_array_test.cc b/src/tint/transform/decompose_strided_array_test.cc
index 58908fb..96a95fc 100644
--- a/src/tint/transform/decompose_strided_array_test.cc
+++ b/src/tint/transform/decompose_strided_array_test.cc
@@ -39,7 +39,7 @@
     // var<private> arr : array<f32, 4u>
 
     ProgramBuilder b;
-    b.GlobalVar("arr", b.ty.array<f32, 4u>(), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(), ast::AddressSpace::kPrivate);
     EXPECT_FALSE(ShouldRun<DecomposeStridedArray>(Program(std::move(b))));
 }
 
@@ -47,7 +47,7 @@
     // var<private> arr : @stride(4) array<f32, 4u>
 
     ProgramBuilder b;
-    b.GlobalVar("arr", b.ty.array<f32, 4u>(4), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(4), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(ShouldRun<DecomposeStridedArray>(Program(std::move(b))));
 }
 
@@ -55,7 +55,7 @@
     // var<private> arr : @stride(16) array<f32, 4u>
 
     ProgramBuilder b;
-    b.GlobalVar("arr", b.ty.array<f32, 4u>(16), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(16), ast::AddressSpace::kPrivate);
     EXPECT_TRUE(ShouldRun<DecomposeStridedArray>(Program(std::move(b))));
 }
 
@@ -78,7 +78,7 @@
     // }
 
     ProgramBuilder b;
-    b.GlobalVar("arr", b.ty.array<f32, 4u>(4), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(4), ast::AddressSpace::kPrivate);
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(4), b.Expr("arr"))),
@@ -114,7 +114,7 @@
     // }
 
     ProgramBuilder b;
-    b.GlobalVar("arr", b.ty.array<f32, 4u>(32), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(32), ast::AddressSpace::kPrivate);
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(32), b.Expr("arr"))),
@@ -158,7 +158,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(32), b.MemberAccessor("s", "a"))),
@@ -206,7 +206,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array(b.ty.vec4<f32>(), 4_u, 16))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
     b.Func(
         "f", utils::Empty, b.ty.void_(),
         utils::Vector{
@@ -252,7 +252,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, b.Group(0_a), b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(32), b.MemberAccessor("s", "a"))),
@@ -300,7 +300,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array<f32, 4u>(4))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, b.Group(0_a), b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(4), b.MemberAccessor("s", "a"))),
@@ -344,7 +344,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -398,7 +398,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array<f32, 4u>(4))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -450,7 +450,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -511,7 +511,7 @@
     ProgramBuilder b;
     b.Alias("ARR", b.ty.array<f32, 4u>(32));
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.type_name("ARR"))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -581,7 +581,7 @@
                 b.ty.array(b.ty.type_name("ARR_A"), 3_u, 16),  //
                 4_u, 128));
     auto* S = b.Structure("S", utils::Vector{b.Member("a", b.ty.type_name("ARR_B"))});
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
diff --git a/src/tint/transform/decompose_strided_matrix.cc b/src/tint/transform/decompose_strided_matrix.cc
index 75c96f6..91aed43 100644
--- a/src/tint/transform/decompose_strided_matrix.cc
+++ b/src/tint/transform/decompose_strided_matrix.cc
@@ -71,8 +71,8 @@
     for (auto* node : program->ASTNodes().Objects()) {
         if (auto* str = node->As<ast::Struct>()) {
             auto* str_ty = program->Sem().Get(str);
-            if (!str_ty->UsedAs(ast::StorageClass::kUniform) &&
-                !str_ty->UsedAs(ast::StorageClass::kStorage)) {
+            if (!str_ty->UsedAs(ast::AddressSpace::kUniform) &&
+                !str_ty->UsedAs(ast::AddressSpace::kStorage)) {
                 continue;
             }
             for (auto* member : str_ty->Members()) {
diff --git a/src/tint/transform/decompose_strided_matrix_test.cc b/src/tint/transform/decompose_strided_matrix_test.cc
index 6bfba7c..bd202c9 100644
--- a/src/tint/transform/decompose_strided_matrix_test.cc
+++ b/src/tint/transform/decompose_strided_matrix_test.cc
@@ -76,7 +76,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -132,7 +132,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
     b.Func(
         "f", utils::Empty, b.ty.void_(),
         utils::Vector{
@@ -185,7 +185,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -238,7 +238,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -295,7 +295,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func(
         "f", utils::Empty, b.ty.void_(),
@@ -349,7 +349,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -407,7 +407,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -466,7 +466,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite, b.Group(0_a),
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kStorage, ast::Access::kReadWrite, b.Group(0_a),
                 b.Binding(0_a));
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
@@ -537,7 +537,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kPrivate);
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kPrivate);
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -590,7 +590,7 @@
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kPrivate);
+    b.GlobalVar("s", b.ty.Of(S), ast::AddressSpace::kPrivate);
     b.Func("f", utils::Empty, b.ty.void_(),
            utils::Vector{
                b.Assign(b.MemberAccessor("s", "m"),
diff --git a/src/tint/transform/first_index_offset.cc b/src/tint/transform/first_index_offset.cc
index 918fd5b..cafca32 100644
--- a/src/tint/transform/first_index_offset.cc
+++ b/src/tint/transform/first_index_offset.cc
@@ -121,7 +121,7 @@
 
         // Create a global to hold the uniform buffer
         Symbol buffer_name = ctx.dst->Sym();
-        ctx.dst->GlobalVar(buffer_name, ctx.dst->ty.Of(struct_), ast::StorageClass::kUniform,
+        ctx.dst->GlobalVar(buffer_name, ctx.dst->ty.Of(struct_), ast::AddressSpace::kUniform,
                            utils::Vector{
                                ctx.dst->Binding(AInt(ub_binding)),
                                ctx.dst->Group(AInt(ub_group)),
diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc
index 8286845..8077393 100644
--- a/src/tint/transform/localize_struct_array_assignment.cc
+++ b/src/tint/transform/localize_struct_array_assignment.cc
@@ -62,10 +62,10 @@
         return result;
     }
 
-    // Returns the type and storage class of the originating variable of the lhs
+    // Returns the type and address space of the originating variable of the lhs
     // of the assignment statement.
     // See https://www.w3.org/TR/WGSL/#originating-variable-section
-    std::pair<const sem::Type*, ast::StorageClass> GetOriginatingTypeAndStorageClass(
+    std::pair<const sem::Type*, ast::AddressSpace> GetOriginatingTypeAndAddressSpace(
         const ast::AssignmentStatement* assign_stmt) {
         auto* source_var = ctx.src->Sem().Get(assign_stmt->lhs)->SourceVariable();
         if (!source_var) {
@@ -77,9 +77,9 @@
 
         auto* type = source_var->Type();
         if (auto* ref = type->As<sem::Reference>()) {
-            return {ref->StoreType(), ref->StorageClass()};
+            return {ref->StoreType(), ref->AddressSpace()};
         } else if (auto* ptr = type->As<sem::Pointer>()) {
-            return {ptr->StoreType(), ptr->StorageClass()};
+            return {ptr->StoreType(), ptr->AddressSpace()};
         }
 
         TINT_ICE(Transform, b.Diagnostics())
@@ -111,9 +111,9 @@
             if (!ContainsStructArrayIndex(assign_stmt->lhs)) {
                 return nullptr;
             }
-            auto og = GetOriginatingTypeAndStorageClass(assign_stmt);
-            if (!(og.first->Is<sem::Struct>() && (og.second == ast::StorageClass::kFunction ||
-                                                  og.second == ast::StorageClass::kPrivate))) {
+            auto og = GetOriginatingTypeAndAddressSpace(assign_stmt);
+            if (!(og.first->Is<sem::Struct>() && (og.second == ast::AddressSpace::kFunction ||
+                                                  og.second == ast::AddressSpace::kPrivate))) {
                 return nullptr;
             }
 
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.cc b/src/tint/transform/module_scope_var_to_entry_point_param.cc
index 122fb3c..2004c4c 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.cc
@@ -114,9 +114,9 @@
         // Helper to create an AST node for the store type of the variable.
         auto store_type = [&]() { return CreateASTTypeFor(ctx, ty); };
 
-        ast::StorageClass sc = var->StorageClass();
+        ast::AddressSpace sc = var->AddressSpace();
         switch (sc) {
-            case ast::StorageClass::kHandle: {
+            case ast::AddressSpace::kHandle: {
                 // For a texture or sampler variable, redeclare it as an entry point parameter.
                 // Disable entry point parameter validation.
                 auto* disable_validation =
@@ -128,13 +128,13 @@
 
                 break;
             }
-            case ast::StorageClass::kStorage:
-            case ast::StorageClass::kUniform: {
-                // Variables into the Storage and Uniform storage classes are redeclared as entry
+            case ast::AddressSpace::kStorage:
+            case ast::AddressSpace::kUniform: {
+                // Variables into the Storage and Uniform address spacees are redeclared as entry
                 // point parameters with a pointer type.
                 auto attributes = ctx.Clone(var->Declaration()->attributes);
                 attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter));
-                attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
+                attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
 
                 auto* param_type = store_type();
                 if (auto* arr = ty->As<sem::Array>(); arr && arr->IsRuntimeSized()) {
@@ -157,7 +157,7 @@
 
                 break;
             }
-            case ast::StorageClass::kWorkgroup: {
+            case ast::AddressSpace::kWorkgroup: {
                 if (ContainsMatrix(var->Type())) {
                     // Due to a bug in the MSL compiler, we use a threadgroup memory argument for
                     // any workgroup allocation that contains a matrix. See crbug.com/tint/938.
@@ -173,7 +173,7 @@
                         ctx.dst->MemberAccessor(ctx.dst->Deref(workgroup_param()), member));
                     auto* local_var = ctx.dst->Let(
                         new_var_symbol,
-                        ctx.dst->ty.pointer(store_type(), ast::StorageClass::kWorkgroup),
+                        ctx.dst->ty.pointer(store_type(), ast::AddressSpace::kWorkgroup),
                         member_ptr);
                     ctx.InsertFront(func->body->statements, ctx.dst->Decl(local_var));
                     is_pointer = true;
@@ -182,11 +182,11 @@
                 }
                 [[fallthrough]];
             }
-            case ast::StorageClass::kPrivate: {
-                // Variables in the Private and Workgroup storage classes are redeclared at function
-                // scope. Disable storage class validation on this variable.
+            case ast::AddressSpace::kPrivate: {
+                // Variables in the Private and Workgroup address spacees are redeclared at function
+                // scope. Disable address space validation on this variable.
                 auto* disable_validation =
-                    ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass);
+                    ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace);
                 auto* constructor = ctx.Clone(var->Declaration()->constructor);
                 auto* local_var = ctx.dst->Var(new_var_symbol, store_type(), sc, constructor,
                                                utils::Vector{disable_validation});
@@ -194,15 +194,15 @@
 
                 break;
             }
-            case ast::StorageClass::kPushConstant: {
+            case ast::AddressSpace::kPushConstant: {
                 ctx.dst->Diagnostics().add_error(
                     diag::System::Transform,
-                    "unhandled module-scope storage class (" + utils::ToString(sc) + ")");
+                    "unhandled module-scope address space (" + utils::ToString(sc) + ")");
                 break;
             }
             default: {
                 TINT_ICE(Transform, ctx.dst->Diagnostics())
-                    << "unhandled module-scope storage class (" << sc << ")";
+                    << "unhandled module-scope address space (" << sc << ")";
                 break;
             }
         }
@@ -221,23 +221,23 @@
         auto* var_ast = var->Declaration()->As<ast::Var>();
         auto* ty = var->Type()->UnwrapRef();
         auto* param_type = CreateASTTypeFor(ctx, ty);
-        auto sc = var->StorageClass();
+        auto sc = var->AddressSpace();
         switch (sc) {
-            case ast::StorageClass::kPrivate:
-            case ast::StorageClass::kStorage:
-            case ast::StorageClass::kUniform:
-            case ast::StorageClass::kHandle:
-            case ast::StorageClass::kWorkgroup:
+            case ast::AddressSpace::kPrivate:
+            case ast::AddressSpace::kStorage:
+            case ast::AddressSpace::kUniform:
+            case ast::AddressSpace::kHandle:
+            case ast::AddressSpace::kWorkgroup:
                 break;
-            case ast::StorageClass::kPushConstant: {
+            case ast::AddressSpace::kPushConstant: {
                 ctx.dst->Diagnostics().add_error(
                     diag::System::Transform,
-                    "unhandled module-scope storage class (" + utils::ToString(sc) + ")");
+                    "unhandled module-scope address space (" + utils::ToString(sc) + ")");
                 break;
             }
             default: {
                 TINT_ICE(Transform, ctx.dst->Diagnostics())
-                    << "unhandled module-scope storage class (" << sc << ")";
+                    << "unhandled module-scope address space (" << sc << ")";
             }
         }
 
@@ -247,8 +247,8 @@
             param_type = ctx.dst->ty.pointer(param_type, sc, var_ast->declared_access);
             is_pointer = true;
 
-            // Disable validation of the parameter's storage class and of arguments passed to it.
-            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
+            // Disable validation of the parameter's address space and of arguments passed to it.
+            attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
             attributes.Push(
                 ctx.dst->Disable(ast::DisabledValidation::kIgnoreInvalidPointerArgument));
         }
@@ -311,7 +311,7 @@
 
             bool needs_processing = false;
             for (auto* var : func_sem->TransitivelyReferencedGlobals()) {
-                if (var->StorageClass() != ast::StorageClass::kNone) {
+                if (var->AddressSpace() != ast::AddressSpace::kNone) {
                     needs_processing = true;
                     break;
                 }
@@ -367,7 +367,7 @@
 
             // Process and redeclare all variables referenced by the function.
             for (auto* var : func_sem->TransitivelyReferencedGlobals()) {
-                if (var->StorageClass() == ast::StorageClass::kNone) {
+                if (var->AddressSpace() == ast::AddressSpace::kNone) {
                     continue;
                 }
                 if (local_private_vars_.count(var)) {
@@ -385,7 +385,7 @@
 
                 // Check if this is a private variable that is only referenced by this function.
                 bool local_private = false;
-                if (var->StorageClass() == ast::StorageClass::kPrivate) {
+                if (var->AddressSpace() == ast::AddressSpace::kPrivate) {
                     local_private = true;
                     for (auto* user : var->Users()) {
                         auto* stmt = user->Stmt();
@@ -399,11 +399,11 @@
                 if (local_private) {
                     // Redeclare the variable at function scope.
                     auto* disable_validation =
-                        ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass);
+                        ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace);
                     auto* constructor = ctx.Clone(var->Declaration()->constructor);
                     auto* local_var = ctx.dst->Var(new_var_symbol,
                                                    CreateASTTypeFor(ctx, var->Type()->UnwrapRef()),
-                                                   ast::StorageClass::kPrivate, constructor,
+                                                   ast::AddressSpace::kPrivate, constructor,
                                                    utils::Vector{disable_validation});
                     ctx.InsertFront(func_ast->body->statements, ctx.dst->Decl(local_var));
                     local_private_vars_.insert(var);
@@ -431,7 +431,7 @@
                 auto* str =
                     ctx.dst->Structure(ctx.dst->Sym(), std::move(workgroup_parameter_members));
                 auto* param_type =
-                    ctx.dst->ty.pointer(ctx.dst->ty.Of(str), ast::StorageClass::kWorkgroup);
+                    ctx.dst->ty.pointer(ctx.dst->ty.Of(str), ast::AddressSpace::kWorkgroup);
                 auto* disable_validation =
                     ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter);
                 auto* param = ctx.dst->Param(workgroup_param(), param_type,
@@ -447,8 +447,8 @@
                 // Add new arguments for any variables that are needed by the callee.
                 // For entry points, pass non-handle types as pointers.
                 for (auto* target_var : target_sem->TransitivelyReferencedGlobals()) {
-                    auto sc = target_var->StorageClass();
-                    if (sc == ast::StorageClass::kNone) {
+                    auto sc = target_var->AddressSpace();
+                    if (sc == ast::AddressSpace::kNone) {
                         continue;
                     }
 
@@ -476,10 +476,10 @@
             }
         }
 
-        // Now remove all module-scope variables with these storage classes.
+        // Now remove all module-scope variables with these address spacees.
         for (auto* var_ast : ctx.src->AST().GlobalVariables()) {
             auto* var_sem = ctx.src->Sem().Get(var_ast);
-            if (var_sem->StorageClass() != ast::StorageClass::kNone) {
+            if (var_sem->AddressSpace() != ast::AddressSpace::kNone) {
                 ctx.Remove(ctx.src->AST().GlobalDeclarations(), var_ast);
             }
         }
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.h b/src/tint/transform/module_scope_var_to_entry_point_param.h
index 40e6b7d..75bdaf3 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.h
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.h
@@ -27,7 +27,7 @@
 /// then passes them as pointer parameters to any function that references them.
 ///
 /// Since WGSL does not allow entry point parameters or function-scope variables
-/// to have these storage classes, we annotate the new variable declarations
+/// to have these address spaces, we annotate the new variable declarations
 /// with an attribute that bypasses that validation rule.
 ///
 /// Before:
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
index dcf8912..34058a7 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
@@ -51,8 +51,8 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol : f32;
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol_1 : f32;
   tint_symbol = tint_symbol_1;
 }
 )";
@@ -76,8 +76,8 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol : f32;
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol_1 : f32;
   tint_symbol = tint_symbol_1;
 }
 )";
@@ -121,17 +121,17 @@
 fn no_uses() {
 }
 
-fn zoo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
+fn zoo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
   *(tint_symbol) = (*(tint_symbol) * 2.0);
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
   *(tint_symbol_1) = a;
   *(tint_symbol_2) = b;
   zoo(tint_symbol_1);
 }
 
-fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
+fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
   let b : f32 = 2.0;
   bar(a, b, tint_symbol_3, tint_symbol_4);
   no_uses();
@@ -139,8 +139,8 @@
 
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_5 : f32;
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_6 : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol_5 : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_6 : f32;
   foo(1.0, &(tint_symbol_5), &(tint_symbol_6));
 }
 )";
@@ -183,12 +183,12 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_5 : f32;
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_6 : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol_5 : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_6 : f32;
   foo(1.0, &(tint_symbol_5), &(tint_symbol_6));
 }
 
-fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
+fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
   let b : f32 = 2.0;
   bar(a, b, tint_symbol_3, tint_symbol_4);
   no_uses();
@@ -197,13 +197,13 @@
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
   *(tint_symbol_1) = a;
   *(tint_symbol_2) = b;
   zoo(tint_symbol_1);
 }
 
-fn zoo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
+fn zoo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
   *(tint_symbol) = (*(tint_symbol) * 2.0);
 }
 )";
@@ -227,8 +227,8 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32 = 1.0;
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32 = f32();
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol : f32 = 1.0;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol_1 : f32 = f32();
   let x : f32 = (tint_symbol + tint_symbol_1);
 }
 )";
@@ -252,8 +252,8 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32 = 1.0;
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32 = f32();
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol : f32 = 1.0;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol_1 : f32 = f32();
   let x : f32 = (tint_symbol + tint_symbol_1);
 }
 )";
@@ -280,8 +280,8 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_1 : f32;
   let p_ptr : ptr<private, f32> = &(tint_symbol);
   let w_ptr : ptr<workgroup, f32> = &(tint_symbol_1);
   let x : f32 = (*(p_ptr) + *(w_ptr));
@@ -311,8 +311,8 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_1 : f32;
   let p_ptr : ptr<private, f32> = &(tint_symbol);
   let w_ptr : ptr<workgroup, f32> = &(tint_symbol_1);
   let x : f32 = (*(p_ptr) + *(w_ptr));
@@ -348,13 +348,13 @@
   *(p) = 0.0;
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
+fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
   bar(tint_symbol);
 }
 
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_1 : f32;
   foo(&(tint_symbol_1));
 }
 )";
@@ -385,11 +385,11 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_address_space) var<workgroup> tint_symbol_1 : f32;
   foo(&(tint_symbol_1));
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
+fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
   bar(tint_symbol);
 }
 
@@ -427,7 +427,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, S>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, S>) {
   _ = *(tint_symbol);
   _ = *(tint_symbol_1);
 }
@@ -457,7 +457,7 @@
 
     auto* expect = R"(
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, S>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, S>) {
   _ = *(tint_symbol);
   _ = *(tint_symbol_1);
 }
@@ -489,7 +489,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
   _ = (*(tint_symbol)).arr[0];
 }
 )";
@@ -516,7 +516,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
   _ = (*(tint_symbol)).arr[0];
 }
 )";
@@ -546,12 +546,12 @@
   arr : array<f32>,
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>>) {
+fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>>) {
   _ = (*(tint_symbol))[0];
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, tint_symbol_2>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, tint_symbol_2>) {
   foo(&((*(tint_symbol_1)).arr));
 }
 )";
@@ -581,11 +581,11 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, tint_symbol_2>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_1 : ptr<storage, tint_symbol_2>) {
   foo(&((*(tint_symbol_1)).arr));
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>>) {
+fn foo(@internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>>) {
   _ = (*(tint_symbol))[0];
 }
 )";
@@ -616,7 +616,7 @@
 type myarray = array<f32>;
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
   _ = (*(tint_symbol)).arr[0];
 }
 )";
@@ -644,7 +644,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
   _ = (*(tint_symbol)).arr[0];
 }
 
@@ -681,7 +681,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
   _ = (*(tint_symbol)).arr[0];
 }
 )";
@@ -715,7 +715,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
   _ = (*(tint_symbol)).arr[0];
 }
 )";
@@ -765,12 +765,12 @@
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S>) {
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S>) {
   _ = *(tint_symbol);
   _ = *(tint_symbol_1);
 }
 
-fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S>) {
+fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S>) {
   let b : f32 = 2.0;
   _ = *(tint_symbol_2);
   bar(a, b, tint_symbol_2, tint_symbol_3);
@@ -778,7 +778,7 @@
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_5 : ptr<storage, S>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_5 : ptr<storage, S>) {
   foo(1.0, tint_symbol_4, tint_symbol_5);
 }
 )";
@@ -822,11 +822,11 @@
 
     auto* expect = R"(
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_5 : ptr<storage, S>) {
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol_5 : ptr<storage, S>) {
   foo(1.0, tint_symbol_4, tint_symbol_5);
 }
 
-fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S>) {
+fn foo(a : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S>) {
   let b : f32 = 2.0;
   _ = *(tint_symbol_2);
   bar(a, b, tint_symbol_2, tint_symbol_3);
@@ -836,7 +836,7 @@
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S>) {
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_address_space) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S>) {
   _ = *(tint_symbol);
   _ = *(tint_symbol_1);
 }
@@ -1192,7 +1192,7 @@
 
     auto* expect = R"(
 fn foo(a : f32) -> f32 {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol : f32;
   let x = tint_symbol;
   tint_symbol = (x * a);
   return tint_symbol;
@@ -1235,7 +1235,7 @@
 }
 
 fn foo(a : f32) -> f32 {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+  @internal(disable_validation__ignore_address_space) var<private> tint_symbol : f32;
   let x = tint_symbol;
   tint_symbol = (x * a);
   return tint_symbol;
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index d75e7a2..83ca3bd 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -133,7 +133,7 @@
                         b.Group(AInt(bps.plane_1.group)), b.Binding(AInt(bps.plane_1.binding)));
             syms.params = b.Symbols().New("ext_tex_params");
             b.GlobalVar(syms.params, b.ty.type_name("ExternalTextureParams"),
-                        ast::StorageClass::kUniform, b.Group(AInt(bps.params.group)),
+                        ast::AddressSpace::kUniform, b.Group(AInt(bps.params.group)),
                         b.Binding(AInt(bps.params.binding)));
 
             // Replace the original texture_external binding with a texture_2d<f32> binding.
diff --git a/src/tint/transform/num_workgroups_from_uniform.cc b/src/tint/transform/num_workgroups_from_uniform.cc
index b96a2ee..2122f07 100644
--- a/src/tint/transform/num_workgroups_from_uniform.cc
+++ b/src/tint/transform/num_workgroups_from_uniform.cc
@@ -149,7 +149,7 @@
             }
 
             num_workgroups_ubo = ctx.dst->GlobalVar(
-                ctx.dst->Sym(), ctx.dst->ty.Of(num_workgroups_struct), ast::StorageClass::kUniform,
+                ctx.dst->Sym(), ctx.dst->ty.Of(num_workgroups_struct), ast::AddressSpace::kUniform,
                 ctx.dst->Group(AInt(group)), ctx.dst->Binding(AInt(binding)));
         }
         return num_workgroups_ubo;
diff --git a/src/tint/transform/pad_structs.cc b/src/tint/transform/pad_structs.cc
index 0c5da5c..34aee11 100644
--- a/src/tint/transform/pad_structs.cc
+++ b/src/tint/transform/pad_structs.cc
@@ -78,7 +78,7 @@
             new_members.Push(ctx.dst->Member(name, type));
 
             uint32_t size = ty->Size();
-            if (ty->Is<sem::Struct>() && str->UsedAs(ast::StorageClass::kUniform)) {
+            if (ty->Is<sem::Struct>() && str->UsedAs(ast::AddressSpace::kUniform)) {
                 // std140 structs should be padded out to 16 bytes.
                 size = utils::RoundUp(16u, size);
             } else if (auto* array_ty = ty->As<sem::Array>()) {
@@ -91,7 +91,7 @@
 
         // Add any required padding after the last member, if it's not a runtime-sized array.
         uint32_t struct_size = str->Size();
-        if (str->UsedAs(ast::StorageClass::kUniform)) {
+        if (str->UsedAs(ast::AddressSpace::kUniform)) {
             struct_size = utils::RoundUp(16u, struct_size);
         }
         if (offset < struct_size && !has_runtime_sized_array) {
diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc
index b9b6c5e..bac4344 100644
--- a/src/tint/transform/robustness.cc
+++ b/src/tint/transform/robustness.cc
@@ -38,8 +38,8 @@
     /// The clone context
     CloneContext& ctx;
 
-    /// Set of storage classes to not apply the transform to
-    std::unordered_set<ast::StorageClass> omitted_classes;
+    /// Set of address spacees to not apply the transform to
+    std::unordered_set<ast::AddressSpace> omitted_classes;
 
     /// Applies the transformation state to `ctx`.
     void Transform() {
@@ -57,7 +57,7 @@
         auto* ret_type = sem->Type();
 
         auto* ref = ret_type->As<sem::Reference>();
-        if (ref && omitted_classes.count(ref->StorageClass()) != 0) {
+        if (ref && omitted_classes.count(ref->AddressSpace()) != 0) {
             return nullptr;
         }
 
@@ -229,14 +229,14 @@
         cfg = *cfg_data;
     }
 
-    std::unordered_set<ast::StorageClass> omitted_classes;
+    std::unordered_set<ast::AddressSpace> omitted_classes;
     for (auto sc : cfg.omitted_classes) {
         switch (sc) {
-            case StorageClass::kUniform:
-                omitted_classes.insert(ast::StorageClass::kUniform);
+            case AddressSpace::kUniform:
+                omitted_classes.insert(ast::AddressSpace::kUniform);
                 break;
-            case StorageClass::kStorage:
-                omitted_classes.insert(ast::StorageClass::kStorage);
+            case AddressSpace::kStorage:
+                omitted_classes.insert(ast::AddressSpace::kStorage);
                 break;
         }
     }
diff --git a/src/tint/transform/robustness.h b/src/tint/transform/robustness.h
index 549b666..21a7ff9 100644
--- a/src/tint/transform/robustness.h
+++ b/src/tint/transform/robustness.h
@@ -33,8 +33,8 @@
 /// (array length - 1).
 class Robustness final : public Castable<Robustness, Transform> {
   public:
-    /// Storage class to be skipped in the transform
-    enum class StorageClass {
+    /// Address space to be skipped in the transform
+    enum class AddressSpace {
         kUniform,
         kStorage,
     };
@@ -54,9 +54,9 @@
         /// @returns this Config
         Config& operator=(const Config&);
 
-        /// Storage classes to omit from apply the transform to.
+        /// Address spacees to omit from apply the transform to.
         /// This allows for optimizing on hardware that provide safe accesses.
-        std::unordered_set<StorageClass> omitted_classes;
+        std::unordered_set<AddressSpace> omitted_classes;
     };
 
     /// Constructor
diff --git a/src/tint/transform/robustness_test.cc b/src/tint/transform/robustness_test.cc
index 4cd4604..fa04e47 100644
--- a/src/tint/transform/robustness_test.cc
+++ b/src/tint/transform/robustness_test.cc
@@ -277,86 +277,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// TODO(crbug.com/tint/1177) - Validation currently forbids arrays larger than
-// 0xffffffff. If WGSL supports 64-bit indexing, re-enable this test.
-TEST_F(RobustnessTest, DISABLED_LargeArrays_Idx) {
-    auto* src = R"(
-struct S {
-  a : array<f32, 0x7fffffff>,
-  b : array<f32>,
-};
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
-  // Signed
-  var i32_a1 : f32 = s.a[ 0x7ffffffe];
-  var i32_a2 : f32 = s.a[ 1];
-  var i32_a3 : f32 = s.a[ 0];
-  var i32_a4 : f32 = s.a[-1];
-  var i32_a5 : f32 = s.a[-0x7fffffff];
-
-  var i32_b1 : f32 = s.b[ 0x7ffffffe];
-  var i32_b2 : f32 = s.b[ 1];
-  var i32_b3 : f32 = s.b[ 0];
-  var i32_b4 : f32 = s.b[-1];
-  var i32_b5 : f32 = s.b[-0x7fffffff];
-
-  // Unsigned
-  var u32_a1 : f32 = s.a[0u];
-  var u32_a2 : f32 = s.a[1u];
-  var u32_a3 : f32 = s.a[0x7ffffffeu];
-  var u32_a4 : f32 = s.a[0x7fffffffu];
-  var u32_a5 : f32 = s.a[0x80000000u];
-  var u32_a6 : f32 = s.a[0xffffffffu];
-
-  var u32_b1 : f32 = s.b[0u];
-  var u32_b2 : f32 = s.b[1u];
-  var u32_b3 : f32 = s.b[0x7ffffffeu];
-  var u32_b4 : f32 = s.b[0x7fffffffu];
-  var u32_b5 : f32 = s.b[0x80000000u];
-  var u32_b6 : f32 = s.b[0xffffffffu];
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : array<f32, 2147483647>,
-  b : array<f32>,
-};
-
-@group(0) @binding(0) var<storage, read> s : S;
-
-fn f() {
-  var i32_a1 : f32 = s.a[2147483646];
-  var i32_a2 : f32 = s.a[1];
-  var i32_a3 : f32 = s.a[0];
-  var i32_a4 : f32 = s.a[0];
-  var i32_a5 : f32 = s.a[0];
-  var i32_b1 : f32 = s.b[min(2147483646u, (arrayLength(&(s.b)) - 1u))];
-  var i32_b2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
-  var i32_b3 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
-  var i32_b4 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
-  var i32_b5 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
-  var u32_a1 : f32 = s.a[0u];
-  var u32_a2 : f32 = s.a[1u];
-  var u32_a3 : f32 = s.a[2147483646u];
-  var u32_a4 : f32 = s.a[2147483646u];
-  var u32_a5 : f32 = s.a[2147483646u];
-  var u32_a6 : f32 = s.a[2147483646u];
-  var u32_b1 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
-  var u32_b2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
-  var u32_b3 : f32 = s.b[min(2147483646u, (arrayLength(&(s.b)) - 1u))];
-  var u32_b4 : f32 = s.b[min(2147483647u, (arrayLength(&(s.b)) - 1u))];
-  var u32_b5 : f32 = s.b[min(2147483648u, (arrayLength(&(s.b)) - 1u))];
-  var u32_b6 : f32 = s.b[min(4294967295u, (arrayLength(&(s.b)) - 1u))];
-}
-)";
-
-    auto got = Run<Robustness>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
 TEST_F(RobustnessTest, Vector_Idx_Scalar) {
     auto* src = R"(
 var<private> a : vec3<f32>;
@@ -701,44 +621,95 @@
 )";
 
     auto got = Run<Robustness>(src);
-
     EXPECT_EQ(expect, str(got));
 }
 
-// TODO(dsinclair): Implement when constant_id exists
-TEST_F(RobustnessTest, DISABLED_Vector_Constant_Id_Clamps) {
-    // @id(1300) override idx : i32;
-    // var a : vec3<f32>
-    // var b : f32 = a[idx]
-    //
-    // ->var b : f32 = a[min(u32(idx), 2)]
+TEST_F(RobustnessTest, Vector_Constant_Id_Clamps) {
+    auto* src = R"(
+@id(1300) override idx : i32;
+fn f() {
+  var a : vec3<f32>;
+  var b : f32 = a[idx];
+}
+)";
+
+    auto* expect = R"(
+@id(1300) override idx : i32;
+
+fn f() {
+  var a : vec3<f32>;
+  var b : f32 = a[min(u32(idx), 2u)];
+}
+)";
+
+    auto got = Run<Robustness>(src);
+    EXPECT_EQ(expect, str(got));
 }
 
-// TODO(dsinclair): Implement when constant_id exists
-TEST_F(RobustnessTest, DISABLED_Array_Constant_Id_Clamps) {
-    // @id(1300) override idx : i32;
-    // var a : array<f32, 4>
-    // var b : f32 = a[idx]
-    //
-    // -> var b : f32 = a[min(u32(idx), 3)]
+TEST_F(RobustnessTest, Array_Constant_Id_Clamps) {
+    auto* src = R"(
+@id(1300) override idx : i32;
+fn f() {
+  var a : array<f32, 4>;
+  var b : f32 = a[idx];
+}
+)";
+
+    auto* expect = R"(
+@id(1300) override idx : i32;
+
+fn f() {
+  var a : array<f32, 4>;
+  var b : f32 = a[min(u32(idx), 3u)];
+}
+)";
+
+    auto got = Run<Robustness>(src);
+    EXPECT_EQ(expect, str(got));
 }
 
-// TODO(dsinclair): Implement when constant_id exists
-TEST_F(RobustnessTest, DISABLED_Matrix_Column_Constant_Id_Clamps) {
-    // @id(1300) override idx : i32;
-    // var a : mat3x2<f32>
-    // var b : f32 = a[idx][1]
-    //
-    // -> var b : f32 = a[min(u32(idx), 2)][1]
+TEST_F(RobustnessTest, Matrix_Column_Constant_Id_Clamps) {
+    auto* src = R"(
+@id(1300) override idx : i32;
+fn f() {
+  var a : mat3x2<f32>;
+  var b : f32 = a[idx][1];
+}
+)";
+
+    auto* expect = R"(
+@id(1300) override idx : i32;
+
+fn f() {
+  var a : mat3x2<f32>;
+  var b : f32 = a[min(u32(idx), 2u)][1];
+}
+)";
+
+    auto got = Run<Robustness>(src);
+    EXPECT_EQ(expect, str(got));
 }
 
-// TODO(dsinclair): Implement when constant_id exists
-TEST_F(RobustnessTest, DISABLED_Matrix_Row_Constant_Id_Clamps) {
-    // @id(1300) override idx : i32;
-    // var a : mat3x2<f32>
-    // var b : f32 = a[1][idx]
-    //
-    // -> var b : f32 = a[1][min(u32(idx), 0, 1)]
+TEST_F(RobustnessTest, Matrix_Row_Constant_Id_Clamps) {
+    auto* src = R"(
+@id(1300) override idx : i32;
+fn f() {
+  var a : mat3x2<f32>;
+  var b : f32 = a[1][idx];
+}
+)";
+
+    auto* expect = R"(
+@id(1300) override idx : i32;
+
+fn f() {
+  var a : mat3x2<f32>;
+  var b : f32 = a[1][min(u32(idx), 1u)];
+}
+)";
+
+    auto got = Run<Robustness>(src);
+    EXPECT_EQ(expect, str(got));
 }
 
 TEST_F(RobustnessTest, RuntimeArray_Clamps) {
@@ -1019,19 +990,33 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// TODO(dsinclair): Test for scoped variables when shadowing is implemented
-TEST_F(RobustnessTest, DISABLED_Shadowed_Variable) {
-    // var a : array<f32, 3>;
-    // var i : u32;
-    // {
-    //    var a : array<f32, 5>;
-    //    var b : f32 = a[i];
-    // }
-    // var c : f32 = a[i];
-    //
-    // -> var b : f32 = a[min(u32(i), 4)];
-    //    var c : f32 = a[min(u32(i), 2)];
-    FAIL();
+TEST_F(RobustnessTest, Shadowed_Variable) {
+    auto* src = R"(
+fn f() {
+  var a : array<f32, 3>;
+  var i : u32;
+  {
+     var a : array<f32, 5>;
+     var b : f32 = a[i];
+  }
+  var c : f32 = a[i];
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var a : array<f32, 3>;
+  var i : u32;
+  {
+    var a : array<f32, 5>;
+    var b : f32 = a[min(i, 4u)];
+  }
+  var c : f32 = a[min(i, 2u)];
+}
+)";
+
+    auto got = Run<Robustness>(src);
+    EXPECT_EQ(expect, str(got));
 }
 
 // Check that existing use of min() and arrayLength() do not get renamed.
@@ -1203,7 +1188,7 @@
 )";
 
     Robustness::Config cfg;
-    cfg.omitted_classes.insert(Robustness::StorageClass::kStorage);
+    cfg.omitted_classes.insert(Robustness::AddressSpace::kStorage);
 
     DataMap data;
     data.Add<Robustness::Config>(cfg);
@@ -1254,7 +1239,7 @@
 )";
 
     Robustness::Config cfg;
-    cfg.omitted_classes.insert(Robustness::StorageClass::kUniform);
+    cfg.omitted_classes.insert(Robustness::AddressSpace::kUniform);
 
     DataMap data;
     data.Add<Robustness::Config>(cfg);
@@ -1305,8 +1290,8 @@
 )";
 
     Robustness::Config cfg;
-    cfg.omitted_classes.insert(Robustness::StorageClass::kStorage);
-    cfg.omitted_classes.insert(Robustness::StorageClass::kUniform);
+    cfg.omitted_classes.insert(Robustness::AddressSpace::kStorage);
+    cfg.omitted_classes.insert(Robustness::AddressSpace::kUniform);
 
     DataMap data;
     data.Add<Robustness::Config>(cfg);
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
index e815633..cafb74c 100644
--- a/src/tint/transform/spirv_atomic.cc
+++ b/src/tint/transform/spirv_atomic.cc
@@ -206,7 +206,7 @@
                 return b.ty.array(AtomicTypeFor(arr->ElemType()), u32(count.value()));
             },
             [&](const sem::Pointer* ptr) {
-                return b.ty.pointer(AtomicTypeFor(ptr->StoreType()), ptr->StorageClass(),
+                return b.ty.pointer(AtomicTypeFor(ptr->StoreType()), ptr->AddressSpace(),
                                     ptr->Access());
             },
             [&](const sem::Reference* ref) { return AtomicTypeFor(ref->StoreType()); },
diff --git a/src/tint/transform/spirv_atomic_test.cc b/src/tint/transform/spirv_atomic_test.cc
index 7f6db34..d9371bf 100644
--- a/src/tint/transform/spirv_atomic_test.cc
+++ b/src/tint/transform/spirv_atomic_test.cc
@@ -548,49 +548,6 @@
     EXPECT_EQ(expect, str(got));
 }
 
-// This sort of mixed usage isn't handled yet. Not sure if we need to just yet.
-// If we don't, then the transform should give sensible diagnostics instead of producing invalid
-// WGSL.
-// TODO(crbug.com/tint/1595)
-TEST_F(SpirvAtomicTest, DISABLED_StructComplexMixedUsage) {
-    auto* src = R"(
-struct S {
-  i : i32,
-}
-
-@group(0) @binding(1) var<storage, read_write> s : S;
-
-fn f() {
-  let x : i32 = s.i;
-  stub_atomicStore_i32(s.i, 1i);
-  s.i = 3i;
-}
-)";
-
-    auto* expect =
-        R"(
-struct S_atomic {
-  i : atomic<i32>,
-}
-
-struct S {
-  i : i32,
-}
-
-@group(0) @binding(1) var<storage, read_write> s : S_atomic;
-
-fn f() {
-  let x : i32 = atomicLoad(&s.i);
-  stub_atomicStore_i32(s.i, 1i);
-  atomicStore(&(s.i), 1i);
-}
-)";
-
-    auto got = Run(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
 TEST_F(SpirvAtomicTest, AtomicLoad) {
     auto* src = R"(
 var<workgroup> wg_u32 : u32;
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index 1f495f0..17f5a5b 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -137,7 +137,7 @@
         // Scan structures for members that need forking
         for (auto* ty : program->Types()) {
             if (auto* str = ty->As<sem::Struct>()) {
-                if (str->UsedAs(ast::StorageClass::kUniform)) {
+                if (str->UsedAs(ast::AddressSpace::kUniform)) {
                     for (auto* member : str->Members()) {
                         if (needs_fork(member->Type())) {
                             return true;
@@ -150,7 +150,7 @@
         // Scan uniform variables that have types that need forking
         for (auto* decl : program->AST().GlobalVariables()) {
             auto* global = program->Sem().Get(decl);
-            if (global->StorageClass() == ast::StorageClass::kUniform) {
+            if (global->AddressSpace() == ast::AddressSpace::kUniform) {
                 if (needs_fork(global->Type()->UnwrapRef())) {
                     return true;
                 }
@@ -269,7 +269,7 @@
         for (auto* global : ctx.src->Sem().Module()->DependencyOrderedDeclarations()) {
             // Check to see if this is a structure used by a uniform buffer...
             auto* str = sem.Get<sem::Struct>(global);
-            if (str && str->UsedAs(ast::StorageClass::kUniform)) {
+            if (str && str->UsedAs(ast::AddressSpace::kUniform)) {
                 // Should this uniform buffer be forked for std140 usage?
                 bool fork_std140 = false;
                 utils::Vector<const ast::StructMember*, 8> members;
@@ -339,7 +339,7 @@
     void ReplaceUniformVarTypes() {
         for (auto* global : ctx.src->AST().GlobalVariables()) {
             if (auto* var = global->As<ast::Var>()) {
-                if (var->declared_storage_class == ast::StorageClass::kUniform) {
+                if (var->declared_address_space == ast::AddressSpace::kUniform) {
                     auto* v = sem.Get(var);
                     if (auto* std140_ty = Std140Type(v->Type()->UnwrapRef())) {
                         ctx.Replace(global->type, std140_ty);
@@ -460,7 +460,7 @@
                 // The matrix was @align() annotated with a larger alignment
                 // than the natural alignment for the matrix. This extra padding
                 // needs to be applied to the first column vector.
-                attributes.Push(b.MemberAlign(u32(align)));
+                attributes.Push(b.MemberAlign(i32(align)));
             }
             if ((i == num_columns - 1) && mat->Size() != size) {
                 // The matrix was @size() annotated with a larger size than the
diff --git a/src/tint/transform/std140.h b/src/tint/transform/std140.h
index f41b1e3..ec5cad5 100644
--- a/src/tint/transform/std140.h
+++ b/src/tint/transform/std140.h
@@ -19,7 +19,7 @@
 
 namespace tint::transform {
 
-/// Std140 is a transform that forks types used in the uniform storage class that contain
+/// Std140 is a transform that forks types used in the uniform address space that contain
 /// `matNx2<f32>` matrices into `N`x`vec2<f32>` column vectors. Types that transitively use these
 /// forked types are also forked. `var<uniform>` variables will use these forked types, and
 /// expressions loading from these variables will do appropriate conversions to the regular WGSL
diff --git a/src/tint/transform/std140_test.cc b/src/tint/transform/std140_test.cc
index 97936ad..6e0b170 100644
--- a/src/tint/transform/std140_test.cc
+++ b/src/tint/transform/std140_test.cc
@@ -200,7 +200,7 @@
 
 struct S_std140 {
   before : i32,
-  @align(128u)
+  @align(128i)
   m_0 : vec2<f32>,
   m_1 : vec2<f32>,
   m_2 : vec2<f32>,
@@ -272,7 +272,7 @@
 
 struct S_std140 {
   before : i32,
-  @align(128u)
+  @align(128i)
   m_0 : vec2<f32>,
   m_1 : vec2<f32>,
   @size(112)
diff --git a/src/tint/transform/unshadow.cc b/src/tint/transform/unshadow.cc
index 952a88b..1746441 100644
--- a/src/tint/transform/unshadow.cc
+++ b/src/tint/transform/unshadow.cc
@@ -57,7 +57,7 @@
             return Switch(
                 decl,  //
                 [&](const ast::Var* var) {
-                    return ctx.dst->Var(source, symbol, type, var->declared_storage_class,
+                    return ctx.dst->Var(source, symbol, type, var->declared_address_space,
                                         var->declared_access, constructor, attributes);
                 },
                 [&](const ast::Let*) {
diff --git a/src/tint/transform/unwind_discard_functions.cc b/src/tint/transform/unwind_discard_functions.cc
index f94f549..7d24e7c 100644
--- a/src/tint/transform/unwind_discard_functions.cc
+++ b/src/tint/transform/unwind_discard_functions.cc
@@ -55,7 +55,7 @@
         if (!module_discard_var_name.IsValid()) {
             module_discard_var_name = b.Symbols().New("tint_discard");
             ctx.dst->GlobalVar(module_discard_var_name, b.ty.bool_(), b.Expr(false),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
         }
         return module_discard_var_name;
     }
diff --git a/src/tint/transform/vertex_pulling.cc b/src/tint/transform/vertex_pulling.cc
index 3c0dce9..2ec12d5 100644
--- a/src/tint/transform/vertex_pulling.cc
+++ b/src/tint/transform/vertex_pulling.cc
@@ -260,7 +260,7 @@
         for (uint32_t i = 0; i < cfg.vertex_state.size(); ++i) {
             // The decorated variable with struct type
             ctx.dst->GlobalVar(GetVertexBufferName(i), ctx.dst->ty.Of(struct_type),
-                               ast::StorageClass::kStorage, ast::Access::kRead,
+                               ast::AddressSpace::kStorage, ast::Access::kRead,
                                ctx.dst->Binding(AInt(i)), ctx.dst->Group(AInt(cfg.pulling_group)));
         }
     }
diff --git a/src/tint/transform/zero_init_workgroup_memory.cc b/src/tint/transform/zero_init_workgroup_memory.cc
index 0d3ed98..47a5b99 100644
--- a/src/tint/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/transform/zero_init_workgroup_memory.cc
@@ -122,7 +122,7 @@
         // workgroup storage variables used by `fn`. This will populate #statements.
         auto* func = sem.Get(fn);
         for (auto* var : func->TransitivelyReferencedGlobals()) {
-            if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
+            if (var->AddressSpace() == ast::AddressSpace::kWorkgroup) {
                 BuildZeroingStatements(var->Type()->UnwrapRef(), [&](uint32_t num_values) {
                     auto var_name = ctx.Clone(var->Declaration()->symbol);
                     return Expression{b.Expr(var_name), num_values, ArrayIndices{}};
@@ -427,7 +427,7 @@
 bool ZeroInitWorkgroupMemory::ShouldRun(const Program* program, const DataMap&) const {
     for (auto* global : program->AST().GlobalVariables()) {
         if (auto* var = global->As<ast::Var>()) {
-            if (var->declared_storage_class == ast::StorageClass::kWorkgroup) {
+            if (var->declared_address_space == ast::AddressSpace::kWorkgroup) {
                 return true;
             }
         }
diff --git a/src/tint/writer/append_vector.cc b/src/tint/writer/append_vector.cc
index 527560e..edf9132 100644
--- a/src/tint/writer/append_vector.cc
+++ b/src/tint/writer/append_vector.cc
@@ -137,7 +137,7 @@
         auto* scalar_cast_target = b->create<sem::TypeConversion>(
             packed_el_sem_ty,
             b->create<sem::Parameter>(nullptr, 0u, scalar_sem->Type()->UnwrapRef(),
-                                      ast::StorageClass::kNone, ast::Access::kUndefined),
+                                      ast::AddressSpace::kNone, ast::Access::kUndefined),
             sem::EvaluationStage::kRuntime);
         auto* scalar_cast_sem = b->create<sem::Call>(
             scalar_cast_ast, scalar_cast_target, sem::EvaluationStage::kRuntime,
@@ -158,7 +158,7 @@
                          [&](const tint::sem::Expression* arg, size_t i) -> const sem::Parameter* {
                              return b->create<sem::Parameter>(
                                  nullptr, static_cast<uint32_t>(i), arg->Type()->UnwrapRef(),
-                                 ast::StorageClass::kNone, ast::Access::kUndefined);
+                                 ast::AddressSpace::kNone, ast::Access::kUndefined);
                          }),
         sem::EvaluationStage::kRuntime);
     auto* constructor_sem =
diff --git a/src/tint/writer/append_vector_test.cc b/src/tint/writer/append_vector_test.cc
index a8608f9..9b78b57 100644
--- a/src/tint/writer/append_vector_test.cc
+++ b/src/tint/writer/append_vector_test.cc
@@ -250,7 +250,7 @@
 
 // AppendVector(vec_12, 3) -> vec3<i32>(vec_12, 3)
 TEST_F(AppendVectorTest, Vec2i32Var_i32) {
-    GlobalVar("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr(3_i);
     WrapInFunction(vec_12, scalar_3);
@@ -286,7 +286,7 @@
 
 // AppendVector(1, 2, scalar_3) -> vec3<i32>(1, 2, scalar_3)
 TEST_F(AppendVectorTest, Vec2i32_i32Var) {
-    GlobalVar("scalar_3", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("scalar_3", ty.i32(), ast::AddressSpace::kPrivate);
     auto* scalar_1 = Expr(1_i);
     auto* scalar_2 = Expr(2_i);
     auto* scalar_3 = Expr("scalar_3");
@@ -327,8 +327,8 @@
 
 // AppendVector(vec_12, scalar_3) -> vec3<i32>(vec_12, scalar_3)
 TEST_F(AppendVectorTest, Vec2i32Var_i32Var) {
-    GlobalVar("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    GlobalVar("scalar_3", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("scalar_3", ty.i32(), ast::AddressSpace::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
@@ -364,8 +364,8 @@
 
 // AppendVector(vec_12, scalar_3) -> vec3<i32>(vec_12, i32(scalar_3))
 TEST_F(AppendVectorTest, Vec2i32Var_f32Var) {
-    GlobalVar("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    GlobalVar("scalar_3", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("scalar_3", ty.f32(), ast::AddressSpace::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
@@ -405,8 +405,8 @@
 
 // AppendVector(vec_12, scalar_3) -> vec3<bool>(vec_12, scalar_3)
 TEST_F(AppendVectorTest, Vec2boolVar_boolVar) {
-    GlobalVar("vec_12", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    GlobalVar("scalar_3", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<bool>(), ast::AddressSpace::kPrivate);
+    GlobalVar("scalar_3", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
diff --git a/src/tint/writer/flatten_bindings_test.cc b/src/tint/writer/flatten_bindings_test.cc
index 64c775f..48d765f 100644
--- a/src/tint/writer/flatten_bindings_test.cc
+++ b/src/tint/writer/flatten_bindings_test.cc
@@ -38,9 +38,9 @@
 
 TEST_F(FlattenBindingsTest, AlreadyFlat) {
     ProgramBuilder b;
-    b.GlobalVar("a", b.ty.i32(), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
-    b.GlobalVar("b", b.ty.i32(), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(1_a));
-    b.GlobalVar("c", b.ty.i32(), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(2_a));
+    b.GlobalVar("a", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("b", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(1_a));
+    b.GlobalVar("c", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(2_a));
 
     Program program(std::move(b));
     ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
@@ -51,9 +51,9 @@
 
 TEST_F(FlattenBindingsTest, NotFlat_SingleNamespace) {
     ProgramBuilder b;
-    b.GlobalVar("a", b.ty.i32(), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
-    b.GlobalVar("b", b.ty.i32(), ast::StorageClass::kUniform, b.Group(1_a), b.Binding(1_a));
-    b.GlobalVar("c", b.ty.i32(), ast::StorageClass::kUniform, b.Group(2_a), b.Binding(2_a));
+    b.GlobalVar("a", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("b", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(1_a), b.Binding(1_a));
+    b.GlobalVar("c", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(2_a), b.Binding(2_a));
     b.WrapInFunction(b.Expr("a"), b.Expr("b"), b.Expr("c"));
 
     Program program(std::move(b));
@@ -84,9 +84,9 @@
     ProgramBuilder b;
 
     const size_t num_buffers = 3;
-    b.GlobalVar("buffer1", b.ty.i32(), ast::StorageClass::kUniform, b.Group(0_a), b.Binding(0_a));
-    b.GlobalVar("buffer2", b.ty.i32(), ast::StorageClass::kStorage, b.Group(1_a), b.Binding(1_a));
-    b.GlobalVar("buffer3", b.ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+    b.GlobalVar("buffer1", b.ty.i32(), ast::AddressSpace::kUniform, b.Group(0_a), b.Binding(0_a));
+    b.GlobalVar("buffer2", b.ty.i32(), ast::AddressSpace::kStorage, b.Group(1_a), b.Binding(1_a));
+    b.GlobalVar("buffer3", b.ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead,
                 b.Group(2_a), b.Binding(2_a));
 
     const size_t num_samplers = 2;
diff --git a/src/tint/writer/float_to_string_test.cc b/src/tint/writer/float_to_string_test.cc
index b629b50..8e5f244 100644
--- a/src/tint/writer/float_to_string_test.cc
+++ b/src/tint/writer/float_to_string_test.cc
@@ -186,25 +186,5 @@
     EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 255, 0)), "-0x1p+128");
 }
 
-// TODO(dneto): It's unclear how Infinity and NaN should be handled.
-// https://github.com/gpuweb/gpuweb/issues/1769
-// Windows x86-64 sets the high mantissa bit on NaNs.
-// Disable NaN tests for now.
-
-TEST(FloatToBitPreservingStringTest, DISABLED_NaN_MsbOnly) {
-    EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 255, 0x400000)), "0x1.8p+128");
-    EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 255, 0x400000)), "-0x1.8p+128");
-}
-
-TEST(FloatToBitPreservingStringTest, DISABLED_NaN_LsbOnly) {
-    EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 255, 0x1)), "0x1.000002p+128");
-    EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 255, 0x1)), "-0x1.000002p+128");
-}
-
-TEST(FloatToBitPreservingStringTest, DISABLED_NaN_NonMsb) {
-    EXPECT_EQ(FloatToBitPreservingString(MakeFloat(0, 255, 0x20101f)), "0x1.40203ep+128");
-    EXPECT_EQ(FloatToBitPreservingString(MakeFloat(1, 255, 0x20101f)), "-0x1.40203ep+128");
-}
-
 }  // namespace
 }  // namespace tint::writer
diff --git a/src/tint/writer/generate_external_texture_bindings_test.cc b/src/tint/writer/generate_external_texture_bindings_test.cc
index 718ab43..4fc1934 100644
--- a/src/tint/writer/generate_external_texture_bindings_test.cc
+++ b/src/tint/writer/generate_external_texture_bindings_test.cc
@@ -23,7 +23,7 @@
 
 using namespace tint::number_suffixes;  // NOLINT
 
-constexpr auto kUniform = ast::StorageClass::kUniform;
+constexpr auto kUniform = ast::AddressSpace::kUniform;
 
 class GenerateExternalTextureBindingsTest : public ::testing::Test {};
 
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 16513a9..fabbf9a 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -393,7 +393,7 @@
     } else if (src_type->is_unsigned_scalar_or_vector() && dst_type->is_float_scalar_or_vector()) {
         out << "uintBitsToFloat";
     } else {
-        if (!EmitType(out, dst_type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+        if (!EmitType(out, dst_type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
             return false;
         }
     }
@@ -456,12 +456,12 @@
     auto* uint_type = BoolTypeToUint(bool_type);
 
     // Cast result to bool scalar or vector type.
-    if (!EmitType(out, bool_type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, bool_type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     ScopedParen outerCastParen(out);
     // Cast LHS to uint scalar or vector type.
-    if (!EmitType(out, uint_type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, uint_type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     {
@@ -481,7 +481,7 @@
         return false;
     }
     // Cast RHS to uint scalar or vector type.
-    if (!EmitType(out, uint_type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, uint_type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     {
@@ -508,20 +508,20 @@
                                 std::vector<std::string> parameter_names;
                                 {
                                     auto decl = line(&b);
-                                    if (!EmitTypeAndName(decl, ret_ty, ast::StorageClass::kNone,
+                                    if (!EmitTypeAndName(decl, ret_ty, ast::AddressSpace::kNone,
                                                          ast::Access::kUndefined, fn_name)) {
                                         return "";
                                     }
                                     {
                                         ScopedParen sp(decl);
                                         const auto* ty = TypeOf(expr->lhs)->UnwrapRef();
-                                        if (!EmitTypeAndName(decl, ty, ast::StorageClass::kNone,
+                                        if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
                                                              ast::Access::kUndefined, "lhs")) {
                                             return "";
                                         }
                                         decl << ", ";
                                         ty = TypeOf(expr->rhs)->UnwrapRef();
-                                        if (!EmitTypeAndName(decl, ty, ast::StorageClass::kNone,
+                                        if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
                                                              ast::Access::kUndefined, "rhs")) {
                                             return "";
                                         }
@@ -839,7 +839,7 @@
 bool GeneratorImpl::EmitTypeConversion(std::ostream& out,
                                        const sem::Call* call,
                                        const sem::TypeConversion* conv) {
-    if (!EmitType(out, conv->Target(), ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, conv->Target(), ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     ScopedParen sp(out);
@@ -862,7 +862,7 @@
         return EmitZeroValue(out, type);
     }
 
-    if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     ScopedParen sp(out);
@@ -934,7 +934,7 @@
 
             {
                 auto pre = line();
-                if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::StorageClass::kNone,
+                if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, result)) {
                     return false;
                 }
@@ -1072,7 +1072,7 @@
 bool GeneratorImpl::EmitCountOneBitsCall(std::ostream& out, const ast::CallExpression* expr) {
     // GLSL's bitCount returns an integer type, so cast it to the appropriate
     // unsigned type.
-    if (!EmitType(out, TypeOf(expr)->UnwrapRef(), ast::StorageClass::kNone, ast::Access::kReadWrite,
+    if (!EmitType(out, TypeOf(expr)->UnwrapRef(), ast::AddressSpace::kNone, ast::Access::kReadWrite,
                   "")) {
         return false;
     }
@@ -1144,7 +1144,7 @@
             std::string v;
             {
                 std::stringstream s;
-                if (!EmitType(s, vec_ty->type(), ast::StorageClass::kNone, ast::Access::kRead,
+                if (!EmitType(s, vec_ty->type(), ast::AddressSpace::kNone, ast::Access::kRead,
                               "")) {
                     return "";
                 }
@@ -1152,16 +1152,16 @@
             }
             {  // (u)int tint_int_dot([i|u]vecN a, [i|u]vecN b) {
                 auto l = line(&b);
-                if (!EmitType(l, vec_ty->type(), ast::StorageClass::kNone, ast::Access::kRead,
+                if (!EmitType(l, vec_ty->type(), ast::AddressSpace::kNone, ast::Access::kRead,
                               "")) {
                     return "";
                 }
                 l << " " << fn_name << "(";
-                if (!EmitType(l, vec_ty, ast::StorageClass::kNone, ast::Access::kRead, "")) {
+                if (!EmitType(l, vec_ty, ast::AddressSpace::kNone, ast::Access::kRead, "")) {
                     return "";
                 }
                 l << " a, ";
-                if (!EmitType(l, vec_ty, ast::StorageClass::kNone, ast::Access::kRead, "")) {
+                if (!EmitType(l, vec_ty, ast::AddressSpace::kNone, ast::Access::kRead, "")) {
                     return "";
                 }
                 l << " b) {";
@@ -1212,7 +1212,7 @@
 
             {
                 auto l = line(b);
-                if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
+                if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
                               ast::Access::kUndefined, "")) {
                     return false;
                 }
@@ -1238,7 +1238,7 @@
 
             {
                 auto l = line(b);
-                if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
+                if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
                               ast::Access::kUndefined, "")) {
                     return false;
                 }
@@ -1802,7 +1802,7 @@
     {
         auto out = line();
         auto name = builder_.Symbols().NameFor(func->symbol);
-        if (!EmitType(out, sem->ReturnType(), ast::StorageClass::kNone, ast::Access::kReadWrite,
+        if (!EmitType(out, sem->ReturnType(), ast::AddressSpace::kNone, ast::Access::kReadWrite,
                       "")) {
             return false;
         }
@@ -1828,13 +1828,13 @@
                 type = ptr->StoreType();
             }
 
-            // Note: WGSL only allows for StorageClass::kNone on parameters, however
+            // Note: WGSL only allows for AddressSpace::kNone on parameters, however
             // the sanitizer transforms generates load / store functions for storage
             // or uniform buffers. These functions have a buffer parameter with
-            // StorageClass::kStorage or StorageClass::kUniform. This is required to
+            // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
             // correctly translate the parameter to a [RW]ByteAddressBuffer for
             // storage buffers and a uint4[N] for uniform buffers.
-            if (!EmitTypeAndName(out, type, v->StorageClass(), v->Access(),
+            if (!EmitTypeAndName(out, type, v->AddressSpace(), v->Access(),
                                  builder_.Symbols().NameFor(v->Declaration()->symbol))) {
                 return false;
             }
@@ -1857,28 +1857,28 @@
         global,  //
         [&](const ast::Var* var) {
             auto* sem = builder_.Sem().Get<sem::GlobalVariable>(global);
-            switch (sem->StorageClass()) {
-                case ast::StorageClass::kUniform:
+            switch (sem->AddressSpace()) {
+                case ast::AddressSpace::kUniform:
                     return EmitUniformVariable(var, sem);
-                case ast::StorageClass::kStorage:
+                case ast::AddressSpace::kStorage:
                     return EmitStorageVariable(var, sem);
-                case ast::StorageClass::kHandle:
+                case ast::AddressSpace::kHandle:
                     return EmitHandleVariable(var, sem);
-                case ast::StorageClass::kPrivate:
+                case ast::AddressSpace::kPrivate:
                     return EmitPrivateVariable(sem);
-                case ast::StorageClass::kWorkgroup:
+                case ast::AddressSpace::kWorkgroup:
                     return EmitWorkgroupVariable(sem);
-                case ast::StorageClass::kIn:
-                case ast::StorageClass::kOut:
+                case ast::AddressSpace::kIn:
+                case ast::AddressSpace::kOut:
                     return EmitIOVariable(sem);
-                case ast::StorageClass::kPushConstant:
+                case ast::AddressSpace::kPushConstant:
                     diagnostics_.add_error(
                         diag::System::Writer,
-                        "unhandled storage class " + utils::ToString(sem->StorageClass()));
+                        "unhandled address space " + utils::ToString(sem->AddressSpace()));
                     return false;
                 default: {
                     TINT_ICE(Writer, diagnostics_)
-                        << "unhandled storage class " << sem->StorageClass();
+                        << "unhandled address space " << sem->AddressSpace();
                     return false;
                 }
             }
@@ -1952,7 +1952,7 @@
     if (auto* storage = type->As<sem::StorageTexture>()) {
         out << "layout(" << convert_texel_format_to_glsl(storage->texel_format()) << ") ";
     }
-    if (!EmitTypeAndName(out, type, sem->StorageClass(), sem->Access(), name)) {
+    if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
         return false;
     }
 
@@ -1966,7 +1966,7 @@
 
     auto name = builder_.Symbols().NameFor(decl->symbol);
     auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->StorageClass(), var->Access(), name)) {
+    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
         return false;
     }
 
@@ -1993,7 +1993,7 @@
 
     auto name = builder_.Symbols().NameFor(decl->symbol);
     auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->StorageClass(), var->Access(), name)) {
+    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
         return false;
     }
 
@@ -2026,7 +2026,7 @@
 
     auto name = builder_.Symbols().NameFor(decl->symbol);
     auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->StorageClass(), var->Access(), name)) {
+    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
         return false;
     }
 
@@ -2140,7 +2140,7 @@
             }
             first = false;
 
-            if (!EmitTypeAndName(out, type, sem->StorageClass(), sem->Access(),
+            if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
                                  builder_.Symbols().NameFor(var->symbol))) {
                 return false;
             }
@@ -2197,7 +2197,7 @@
             return true;
         },
         [&](const sem::Vector* v) {
-            if (!EmitType(out, v, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -2218,7 +2218,7 @@
             return true;
         },
         [&](const sem::Matrix* m) {
-            if (!EmitType(out, m, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -2235,7 +2235,7 @@
             return true;
         },
         [&](const sem::Array* a) {
-            if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -2259,7 +2259,7 @@
             return true;
         },
         [&](const sem::Struct* s) {
-            if (!EmitType(out, s, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -2324,7 +2324,7 @@
     } else if (type->Is<sem::U32>()) {
         out << "0u";
     } else if (auto* vec = type->As<sem::Vector>()) {
-        if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+        if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
             return false;
         }
         ScopedParen sp(out);
@@ -2337,7 +2337,7 @@
             }
         }
     } else if (auto* mat = type->As<sem::Matrix>()) {
-        if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+        if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
             return false;
         }
         ScopedParen sp(out);
@@ -2350,7 +2350,7 @@
             }
         }
     } else if (auto* str = type->As<sem::Struct>()) {
-        if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+        if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
             return false;
         }
         bool first = true;
@@ -2364,7 +2364,7 @@
             EmitZeroValue(out, member->Type());
         }
     } else if (auto* arr = type->As<sem::Array>()) {
-        if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+        if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
             return false;
         }
         ScopedParen sp(out);
@@ -2682,24 +2682,24 @@
 
 bool GeneratorImpl::EmitType(std::ostream& out,
                              const sem::Type* type,
-                             ast::StorageClass storage_class,
+                             ast::AddressSpace address_space,
                              ast::Access access,
                              const std::string& name,
                              bool* name_printed /* = nullptr */) {
     if (name_printed) {
         *name_printed = false;
     }
-    switch (storage_class) {
-        case ast::StorageClass::kIn: {
+    switch (address_space) {
+        case ast::AddressSpace::kIn: {
             out << "in ";
             break;
         }
-        case ast::StorageClass::kOut: {
+        case ast::AddressSpace::kOut: {
             out << "out ";
             break;
         }
-        case ast::StorageClass::kUniform:
-        case ast::StorageClass::kHandle: {
+        case ast::AddressSpace::kUniform:
+        case ast::AddressSpace::kHandle: {
             out << "uniform ";
             break;
         }
@@ -2725,7 +2725,7 @@
 
             base_type = arr->ElemType();
         }
-        if (!EmitType(out, base_type, storage_class, access, "")) {
+        if (!EmitType(out, base_type, address_space, access, "")) {
             return false;
         }
         if (!name.empty()) {
@@ -2842,13 +2842,13 @@
             out << "bvec" << width;
         } else {
             out << "vector<";
-            if (!EmitType(out, vec->type(), storage_class, access, "")) {
+            if (!EmitType(out, vec->type(), address_space, access, "")) {
                 return false;
             }
             out << ", " << width << ">";
         }
     } else if (auto* atomic = type->As<sem::Atomic>()) {
-        if (!EmitType(out, atomic->Type(), storage_class, access, name)) {
+        if (!EmitType(out, atomic->Type(), address_space, access, name)) {
             return false;
         }
     } else if (type->Is<sem::Void>()) {
@@ -2863,11 +2863,11 @@
 
 bool GeneratorImpl::EmitTypeAndName(std::ostream& out,
                                     const sem::Type* type,
-                                    ast::StorageClass storage_class,
+                                    ast::AddressSpace address_space,
                                     ast::Access access,
                                     const std::string& name) {
     bool printed_name = false;
-    if (!EmitType(out, type, storage_class, access, name, &printed_name)) {
+    if (!EmitType(out, type, address_space, access, name, &printed_name)) {
         return false;
     }
     if (!name.empty() && !printed_name) {
@@ -2877,7 +2877,7 @@
 }
 
 bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
-    auto storage_class_uses = str->StorageClassUsage();
+    auto address_space_uses = str->AddressSpaceUsage();
     line(b) << "struct " << StructName(str) << " {";
     EmitStructMembers(b, str);
     line(b) << "};";
@@ -2903,7 +2903,7 @@
 
         auto out = line(b);
 
-        if (!EmitTypeAndName(out, ty, ast::StorageClass::kNone, ast::Access::kReadWrite, name)) {
+        if (!EmitTypeAndName(out, ty, ast::AddressSpace::kNone, ast::Access::kReadWrite, name)) {
             return false;
         }
         out << ";";
@@ -2944,7 +2944,7 @@
     auto* type = sem->Type()->UnwrapRef();
 
     auto out = line();
-    if (!EmitTypeAndName(out, type, sem->StorageClass(), sem->Access(),
+    if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
                          builder_.Symbols().NameFor(var->symbol))) {
         return false;
     }
@@ -2971,7 +2971,7 @@
 
     auto out = line();
     // TODO(senorblanco): handle const
-    if (!EmitTypeAndName(out, type, ast::StorageClass::kNone, ast::Access::kUndefined,
+    if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
                          builder_.Symbols().NameFor(let->symbol))) {
         return false;
     }
@@ -2993,7 +2993,7 @@
 
     auto out = line();
     out << "const ";
-    if (!EmitTypeAndName(out, type, ast::StorageClass::kNone, ast::Access::kUndefined,
+    if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
                          builder_.Symbols().NameFor(var->symbol))) {
         return false;
     }
@@ -3020,7 +3020,7 @@
         std::vector<std::string> parameter_names;
         {
             auto decl = line(&b);
-            if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::StorageClass::kNone,
+            if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
                                  ast::Access::kUndefined, fn_name)) {
                 return "";
             }
@@ -3036,7 +3036,7 @@
                         decl << "inout ";
                         ty = ptr->StoreType();
                     }
-                    if (!EmitTypeAndName(decl, ty, ast::StorageClass::kNone,
+                    if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
                                          ast::Access::kUndefined, param_name)) {
                         return "";
                     }
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index 9d1016a..f91775e 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -293,35 +293,35 @@
     /// @returns true on success
     bool EmitGlobalVariable(const ast::Variable* global);
 
-    /// Handles emitting a global variable with the uniform storage class
+    /// Handles emitting a global variable with the uniform address space
     /// @param var the AST node for the 'var'
     /// @param sem the semantic node for the 'var'
     /// @returns true on success
     bool EmitUniformVariable(const ast::Var* var, const sem::Variable* sem);
 
-    /// Handles emitting a global variable with the storage storage class
+    /// Handles emitting a global variable with the storage address space
     /// @param var the AST node for the 'var'
     /// @param sem the semantic node for the 'var'
     /// @returns true on success
     bool EmitStorageVariable(const ast::Var* var, const sem::Variable* sem);
 
-    /// Handles emitting a global variable with the handle storage class
+    /// Handles emitting a global variable with the handle address space
     /// @param var the AST node for the 'var'
     /// @param sem the semantic node for the 'var'
     /// @returns true on success
     bool EmitHandleVariable(const ast::Var* var, const sem::Variable* sem);
 
-    /// Handles emitting a global variable with the private storage class
+    /// Handles emitting a global variable with the private address space
     /// @param var the global variable
     /// @returns true on success
     bool EmitPrivateVariable(const sem::Variable* var);
 
-    /// Handles emitting a global variable with the workgroup storage class
+    /// Handles emitting a global variable with the workgroup address space
     /// @param var the global variable
     /// @returns true on success
     bool EmitWorkgroupVariable(const sem::Variable* var);
 
-    /// Handles emitting a global variable with the input or output storage class
+    /// Handles emitting a global variable with the input or output address space
     /// @param var the global variable
     /// @returns true on success
     bool EmitIOVariable(const sem::GlobalVariable* var);
@@ -394,7 +394,7 @@
     /// Handles generating type
     /// @param out the output stream
     /// @param type the type to generate
-    /// @param storage_class the storage class of the variable
+    /// @param address_space the address space of the variable
     /// @param access the access control type of the variable
     /// @param name the name of the variable, used for array emission.
     /// @param name_printed (optional) if not nullptr and an array was printed
@@ -402,20 +402,20 @@
     /// @returns true if the type is emitted
     bool EmitType(std::ostream& out,
                   const sem::Type* type,
-                  ast::StorageClass storage_class,
+                  ast::AddressSpace address_space,
                   ast::Access access,
                   const std::string& name,
                   bool* name_printed = nullptr);
     /// Handles generating type and name
     /// @param out the output stream
     /// @param type the type to generate
-    /// @param storage_class the storage class of the variable
+    /// @param address_space the address space of the variable
     /// @param access the access control type of the variable
     /// @param name the name to emit
     /// @returns true if the type is emitted
     bool EmitTypeAndName(std::ostream& out,
                          const sem::Type* type,
-                         ast::StorageClass storage_class,
+                         ast::AddressSpace address_space,
                          ast::Access access,
                          const std::string& name);
     /// Handles generating a structure declaration
diff --git a/src/tint/writer/glsl/generator_impl_array_accessor_test.cc b/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
index bbf90fe..bed3dcf 100644
--- a/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
@@ -22,7 +22,7 @@
 using GlslGeneratorImplTest_Expression = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Expression, IndexAccessor) {
-    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::AddressSpace::kPrivate);
     auto* expr = IndexAccessor("ary", 5_i);
     WrapInFunction(expr);
 
diff --git a/src/tint/writer/glsl/generator_impl_assign_test.cc b/src/tint/writer/glsl/generator_impl_assign_test.cc
index 84b6b30..98eae33 100644
--- a/src/tint/writer/glsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/glsl/generator_impl_assign_test.cc
@@ -20,8 +20,8 @@
 using GlslGeneratorImplTest_Assign = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Assign, Emit_Assign) {
-    GlobalVar("lhs", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.i32(), ast::AddressSpace::kPrivate);
     auto* assign = Assign("lhs", "rhs");
     WrapInFunction(assign);
 
diff --git a/src/tint/writer/glsl/generator_impl_binary_test.cc b/src/tint/writer/glsl/generator_impl_binary_test.cc
index 23e2ece..b5e391c 100644
--- a/src/tint/writer/glsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/glsl/generator_impl_binary_test.cc
@@ -43,8 +43,8 @@
         return;
     }
 
-    GlobalVar("left", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -71,8 +71,8 @@
 
     Enable(ast::Extension::kF16);
 
-    GlobalVar("left", ty.f16(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.f16(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.f16(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -90,8 +90,8 @@
 TEST_P(GlslBinaryTest, Emit_u32) {
     auto params = GetParam();
 
-    GlobalVar("left", ty.u32(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.u32(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.u32(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -114,8 +114,8 @@
         return;
     }
 
-    GlobalVar("left", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -151,7 +151,7 @@
                     BinaryData{"(left % right)", ast::BinaryOp::kModulo}));
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_VectorScalar_f32) {
-    GlobalVar("a", vec3<f32>(1_f, 1_f, 1_f), ast::StorageClass::kPrivate);
+    GlobalVar("a", vec3<f32>(1_f, 1_f, 1_f), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("a");
     auto* rhs = Expr(1_f);
 
@@ -169,7 +169,7 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_VectorScalar_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", vec3<f16>(1_h, 1_h, 1_h), ast::StorageClass::kPrivate);
+    GlobalVar("a", vec3<f16>(1_h, 1_h, 1_h), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("a");
     auto* rhs = Expr(1_h);
 
@@ -185,7 +185,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_ScalarVector_f32) {
-    GlobalVar("a", vec3<f32>(1_f, 1_f, 1_f), ast::StorageClass::kPrivate);
+    GlobalVar("a", vec3<f32>(1_f, 1_f, 1_f), ast::AddressSpace::kPrivate);
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("a");
 
@@ -203,7 +203,7 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_ScalarVector_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", vec3<f16>(1_h, 1_h, 1_h), ast::StorageClass::kPrivate);
+    GlobalVar("a", vec3<f16>(1_h, 1_h, 1_h), ast::AddressSpace::kPrivate);
     auto* lhs = Expr(1_h);
     auto* rhs = Expr("a");
 
@@ -219,7 +219,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixScalar_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_f);
 
@@ -236,7 +236,7 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixScalar_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_h);
 
@@ -251,7 +251,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_ScalarMatrix_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("mat");
 
@@ -268,7 +268,7 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_ScalarMatrix_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr(1_h);
     auto* rhs = Expr("mat");
 
@@ -283,7 +283,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixVector_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = vec3<f32>(1_f, 1_f, 1_f);
 
@@ -300,7 +300,7 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixVector_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = vec3<f16>(1_h, 1_h, 1_h);
 
@@ -315,7 +315,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_VectorMatrix_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = vec3<f32>(1_f, 1_f, 1_f);
     auto* rhs = Expr("mat");
 
@@ -332,7 +332,7 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_VectorMatrix_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = vec3<f16>(1_h, 1_h, 1_h);
     auto* rhs = Expr("mat");
 
@@ -347,8 +347,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixMatrix_f32) {
-    GlobalVar("lhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -363,8 +363,8 @@
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixMatrix_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("lhs", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -377,8 +377,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModF32) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -393,8 +393,8 @@
 TEST_F(GlslGeneratorImplTest_Binary, ModF16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.f16(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f16(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f16(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -407,8 +407,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModVec3F32) {
-    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -423,8 +423,8 @@
 TEST_F(GlslGeneratorImplTest_Binary, ModVec3F16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -437,8 +437,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModVec3F32ScalarF32) {
-    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -453,8 +453,8 @@
 TEST_F(GlslGeneratorImplTest_Binary, ModVec3F16ScalarF16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f16(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -467,8 +467,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModScalarF32Vec3F32) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -483,8 +483,8 @@
 TEST_F(GlslGeneratorImplTest_Binary, ModScalarF16Vec3F16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.f16(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f16(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -497,8 +497,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModMixedVec3ScalarF32) {
-    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* expr_vec_mod_vec =
         create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("a"));
@@ -541,8 +541,8 @@
 TEST_F(GlslGeneratorImplTest_Binary, ModMixedVec3ScalarF16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f16(), ast::AddressSpace::kPrivate);
 
     auto* expr_vec_mod_vec =
         create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("a"));
@@ -584,8 +584,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Logical_And) {
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -604,10 +604,10 @@
 
 TEST_F(GlslGeneratorImplTest_Binary, Logical_Multi) {
     // (a && b) || (c || d)
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -636,8 +636,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Logical_Or) {
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -663,9 +663,9 @@
     //   return 3i;
     // }
 
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr =
         If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
@@ -700,9 +700,9 @@
 TEST_F(GlslGeneratorImplTest_Binary, Return_WithLogical) {
     // return (a && b) || c;
 
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = Return(create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -728,10 +728,10 @@
 TEST_F(GlslGeneratorImplTest_Binary, Assign_WithLogical) {
     // a = (b || c) && d;
 
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr =
         Assign(Expr("a"),
@@ -759,9 +759,9 @@
 TEST_F(GlslGeneratorImplTest_Binary, Decl_WithLogical) {
     // var a : bool = (b && c) || d;
 
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* var =
         Var("a", ty.bool_(),
@@ -798,10 +798,10 @@
              Param(Sym(), ty.bool_()),
          },
          ty.void_(), utils::Empty, utils::Empty);
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     utils::Vector params{
         create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index b8161e2..511a309 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -198,19 +198,19 @@
     if (param.type == CallParamType::kF16) {
         Enable(ast::Extension::kF16);
 
-        GlobalVar("h2", ty.vec2<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("h3", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("hm2x2", ty.mat2x2<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("hm3x2", ty.mat3x2<f16>(), ast::StorageClass::kPrivate);
+        GlobalVar("h2", ty.vec2<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("h3", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("hm2x2", ty.mat2x2<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("hm3x2", ty.mat3x2<f16>(), ast::AddressSpace::kPrivate);
     }
 
-    GlobalVar("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
-    GlobalVar("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f2", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("f3", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("u2", ty.vec2<u32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("i2", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b2", ty.vec2<bool>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = GenerateCall(param.builtin, param.type, this);
     ASSERT_NE(nullptr, call) << "Unhandled builtin";
@@ -342,8 +342,8 @@
 TEST_F(GlslGeneratorImplTest_Builtin, Builtin_Call) {
     auto* call = Call("dot", "param1", "param2");
 
-    GlobalVar("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     WrapInFunction(CallStmt(call));
 
@@ -356,33 +356,37 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, Select_Scalar) {
-    auto* call = Call("select", 1_f, 2_f, true);
+    GlobalVar("a", Expr(1_f), ast::AddressSpace::kPrivate);
+    GlobalVar("b", Expr(2_f), ast::AddressSpace::kPrivate);
+    auto* call = Call("select", "a", "b", true);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
     std::stringstream out;
     ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
-    EXPECT_EQ(out.str(), "(true ? 2.0f : 1.0f)");
+    EXPECT_EQ(out.str(), "(true ? b : a)");
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, Select_Vector) {
-    auto* call = Call("select", vec2<i32>(1_i, 2_i), vec2<i32>(3_i, 4_i), vec2<bool>(true, false));
+    GlobalVar("a", vec2<i32>(1_i, 2_i), ast::AddressSpace::kPrivate);
+    GlobalVar("b", vec2<i32>(3_i, 4_i), ast::AddressSpace::kPrivate);
+    auto* call = Call("select", "a", "b", vec2<bool>(true, false));
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
     std::stringstream out;
     ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
-    EXPECT_EQ(out.str(), "mix(ivec2(1, 2), ivec2(3, 4), bvec2(true, false))");
+    EXPECT_EQ(out.str(), "mix(a, b, bvec2(true, false))");
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, FMA_f32) {
     auto* call = Call("fma", "a", "b", "c");
 
-    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     WrapInFunction(CallStmt(call));
 
@@ -397,9 +401,9 @@
 TEST_F(GlslGeneratorImplTest_Builtin, FMA_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("a", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("fma", "a", "b", "c");
     WrapInFunction(CallStmt(call));
@@ -986,7 +990,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack4x8Snorm) {
     auto* call = Call("pack4x8snorm", "p1");
-    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1004,7 +1008,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack4x8Unorm) {
     auto* call = Call("pack4x8unorm", "p1");
-    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1022,7 +1026,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack2x16Snorm) {
     auto* call = Call("pack2x16snorm", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1040,7 +1044,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack2x16Unorm) {
     auto* call = Call("pack2x16unorm", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1058,7 +1062,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack2x16Float) {
     auto* call = Call("pack2x16float", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1076,7 +1080,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack4x8Snorm) {
     auto* call = Call("unpack4x8snorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1094,7 +1098,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack4x8Unorm) {
     auto* call = Call("unpack4x8unorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1112,7 +1116,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack2x16Snorm) {
     auto* call = Call("unpack2x16snorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1130,7 +1134,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack2x16Unorm) {
     auto* call = Call("unpack2x16unorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1148,7 +1152,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack2x16Float) {
     auto* call = Call("unpack2x16float", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -1211,7 +1215,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, DotI32) {
-    GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(Call("dot", "v", "v")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -1237,7 +1241,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, DotU32) {
-    GlobalVar("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.vec3<u32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(Call("dot", "v", "v")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/glsl/generator_impl_call_test.cc b/src/tint/writer/glsl/generator_impl_call_test.cc
index d264896..adf66d3 100644
--- a/src/tint/writer/glsl/generator_impl_call_test.cc
+++ b/src/tint/writer/glsl/generator_impl_call_test.cc
@@ -42,8 +42,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.f32(), utils::Vector{Return(1.23_f)});
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -62,8 +62,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), utils::Empty, utils::Empty);
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = CallStmt(Call("my_func", "param1", "param2"));
     WrapInFunction(call);
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index 6afac16..1357985 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -110,7 +110,7 @@
     // fn f(foo : ptr<function, f32>) -> f32 {
     //   return *foo;
     // }
-    Func("f", utils::Vector{Param("foo", ty.pointer<f32>(ast::StorageClass::kFunction))}, ty.f32(),
+    Func("f", utils::Vector{Param("foo", ty.pointer<f32>(ast::AddressSpace::kFunction))}, ty.f32(),
          utils::Vector{Return(Deref("foo"))});
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -352,7 +352,7 @@
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
     auto* ubo_ty = Structure("UBO", utils::Vector{Member("coord", ty.vec4<f32>())});
     auto* ubo =
-        GlobalVar("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform, Binding(0_a), Group(1_a));
+        GlobalVar("ubo", ty.Of(ubo_ty), ast::AddressSpace::kUniform, Binding(0_a), Group(1_a));
 
     Func("sub_func",
          utils::Vector{
@@ -402,7 +402,7 @@
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_UniformStruct) {
     auto* s = Structure("Uniforms", utils::Vector{Member("coord", ty.vec4<f32>())});
 
-    GlobalVar("uniforms", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a), Group(1_a));
+    GlobalVar("uniforms", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(1_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
 
@@ -442,7 +442,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
@@ -485,7 +485,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(1_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
@@ -529,7 +529,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     Func("frag_main", utils::Empty, ty.void_(),
@@ -570,7 +570,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     Func("frag_main", utils::Empty, ty.void_(),
@@ -607,7 +607,7 @@
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
     auto* s = Structure("S", utils::Vector{Member("x", ty.f32())});
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a), Group(1_a));
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(1_a));
 
     Func("sub_func", utils::Vector{Param("param", ty.f32())}, ty.f32(),
          utils::Vector{
@@ -652,7 +652,7 @@
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
     auto* s = Structure("S", utils::Vector{Member("x", ty.f32())});
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     Func("sub_func", utils::Vector{Param("param", ty.f32())}, ty.f32(),
@@ -859,7 +859,7 @@
 
     auto* s = Structure("Data", utils::Vector{Member("d", ty.f32())});
 
-    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("data", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(0_a));
 
     {
diff --git a/src/tint/writer/glsl/generator_impl_identifier_test.cc b/src/tint/writer/glsl/generator_impl_identifier_test.cc
index ff9bc88..76835c1 100644
--- a/src/tint/writer/glsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/glsl/generator_impl_identifier_test.cc
@@ -20,7 +20,7 @@
 using GlslGeneratorImplTest_Identifier = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Identifier, EmitIdentifierExpression) {
-    GlobalVar("foo", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("foo", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* i = Expr("foo");
     WrapInFunction(i);
diff --git a/src/tint/writer/glsl/generator_impl_if_test.cc b/src/tint/writer/glsl/generator_impl_if_test.cc
index 82dc307..d2368aa 100644
--- a/src/tint/writer/glsl/generator_impl_if_test.cc
+++ b/src/tint/writer/glsl/generator_impl_if_test.cc
@@ -20,7 +20,7 @@
 using GlslGeneratorImplTest_If = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_If, Emit_If) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* cond = Expr("cond");
     auto* body = Block(Return());
@@ -38,8 +38,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_If, Emit_IfWithElseIf) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_cond = Expr("else_cond");
     auto* else_body = Block(Return());
@@ -65,7 +65,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_If, Emit_IfWithElse) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_body = Block(Return());
 
@@ -88,8 +88,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_If, Emit_IfWithMultiple) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_cond = Expr("else_cond");
 
diff --git a/src/tint/writer/glsl/generator_impl_import_test.cc b/src/tint/writer/glsl/generator_impl_import_test.cc
index 9e988f1..b18206c 100644
--- a/src/tint/writer/glsl/generator_impl_import_test.cc
+++ b/src/tint/writer/glsl/generator_impl_import_test.cc
@@ -234,10 +234,6 @@
                                          GlslImportData{"clamp", "clamp"},
                                          GlslImportData{"smoothstep", "smoothstep"}));
 
-TEST_F(GlslGeneratorImplTest_Import, DISABLED_GlslImportData_FMix) {
-    FAIL();
-}
-
 using GlslImportData_TripleParam_Int_Test = TestParamHelper<GlslImportData>;
 TEST_P(GlslImportData_TripleParam_Int_Test, IntScalar) {
     auto param = GetParam();
@@ -256,7 +252,7 @@
                          testing::Values(GlslImportData{"clamp", "clamp"}));
 
 TEST_F(GlslGeneratorImplTest_Import, GlslImportData_Determinant) {
-    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("determinant", "var");
     WrapInFunction(expr);
diff --git a/src/tint/writer/glsl/generator_impl_loop_test.cc b/src/tint/writer/glsl/generator_impl_loop_test.cc
index 1271463..64074ef 100644
--- a/src/tint/writer/glsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/glsl/generator_impl_loop_test.cc
@@ -66,8 +66,8 @@
 TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    GlobalVar("lhs", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* body = Block(create<ast::DiscardStatement>());
     auto* continuing = Block(CallStmt(Call("a_statement")));
@@ -112,7 +112,7 @@
     //   }
     // }
 
-    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.4_f))),  //
                        Decl(Var("other", ty.f32())),             //
diff --git a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
index 43c0d82..a7392cd 100644
--- a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
@@ -91,7 +91,7 @@
 
         auto* s = b.Structure("Data", members);
 
-        b.GlobalVar("data", b.ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+        b.GlobalVar("data", b.ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
                     b.Group(1_a), b.Binding(0_a));
     }
 
@@ -112,7 +112,7 @@
 
 TEST_F(GlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
     auto* s = Structure("Data", utils::Vector{Member("mem", ty.f32())});
-    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::AddressSpace::kPrivate);
 
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(Var("expr", ty.f32(), expr));
diff --git a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
index 2d0d1ce..9c425a8 100644
--- a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
@@ -26,7 +26,7 @@
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -66,7 +66,7 @@
                                          Member(0, "z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -105,7 +105,7 @@
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     auto* p = Let("p", AddressOf("b"));
@@ -230,7 +230,7 @@
     // let p : ptr<function, i32> = &v;
     // let x : i32 = *p;
     auto* v = Var("v", ty.i32());
-    auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(v));
+    auto* p = Let("p", ty.pointer<i32>(ast::AddressSpace::kFunction), AddressOf(v));
     auto* x = Var("x", ty.i32(), Deref(p));
 
     Func("main", utils::Empty, ty.void_(),
@@ -271,11 +271,11 @@
     // let vp : ptr<function, vec4<f32>> = &(*mp)[2i];
     // let v : vec4<f32> = *vp;
     auto* a = Var("a", ty.array(ty.mat4x4<f32>(), 4_u));
-    auto* ap = Let("ap", ty.pointer(ty.array(ty.mat4x4<f32>(), 4_u), ast::StorageClass::kFunction),
+    auto* ap = Let("ap", ty.pointer(ty.array(ty.mat4x4<f32>(), 4_u), ast::AddressSpace::kFunction),
                    AddressOf(a));
-    auto* mp = Let("mp", ty.pointer(ty.mat4x4<f32>(), ast::StorageClass::kFunction),
+    auto* mp = Let("mp", ty.pointer(ty.mat4x4<f32>(), ast::AddressSpace::kFunction),
                    AddressOf(IndexAccessor(Deref(ap), 3_i)));
-    auto* vp = Let("vp", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction),
+    auto* vp = Let("vp", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction),
                    AddressOf(IndexAccessor(Deref(mp), 2_i)));
     auto* v = Var("v", ty.vec4<f32>(), Deref(vp));
 
diff --git a/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc b/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
index f6f127f..59ae98e 100644
--- a/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
@@ -33,11 +33,11 @@
     // @group(0) @binding(0) var<storage, read_write> nephews : Nephews;
     auto* nephews = ctx->Structure(
         "Nephews", utils::Vector{
-                       ctx->Member("huey", ctx->ty.f32(), utils::Vector{ctx->MemberAlign(256_u)}),
-                       ctx->Member("dewey", ctx->ty.f32(), utils::Vector{ctx->MemberAlign(256_u)}),
-                       ctx->Member("louie", ctx->ty.f32(), utils::Vector{ctx->MemberAlign(256_u)}),
+                       ctx->Member("huey", ctx->ty.f32(), utils::Vector{ctx->MemberAlign(256_i)}),
+                       ctx->Member("dewey", ctx->ty.f32(), utils::Vector{ctx->MemberAlign(256_i)}),
+                       ctx->Member("louie", ctx->ty.f32(), utils::Vector{ctx->MemberAlign(256_i)}),
                    });
-    ctx->GlobalVar("nephews", ctx->ty.Of(nephews), ast::StorageClass::kStorage, ctx->Binding(0_a),
+    ctx->GlobalVar("nephews", ctx->ty.Of(nephews), ast::AddressSpace::kStorage, ctx->Binding(0_a),
                    ctx->Group(0_a));
 }
 
diff --git a/src/tint/writer/glsl/generator_impl_switch_test.cc b/src/tint/writer/glsl/generator_impl_switch_test.cc
index c2db3b5..7a2c750 100644
--- a/src/tint/writer/glsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/glsl/generator_impl_switch_test.cc
@@ -22,7 +22,7 @@
 using GlslGeneratorImplTest_Switch = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch) {
-    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* def_body = Block(create<ast::BreakStatement>());
     auto* def = create<ast::CaseStatement>(utils::Empty, def_body);
diff --git a/src/tint/writer/glsl/generator_impl_test.cc b/src/tint/writer/glsl/generator_impl_test.cc
index 919d684..c06b53e 100644
--- a/src/tint/writer/glsl/generator_impl_test.cc
+++ b/src/tint/writer/glsl/generator_impl_test.cc
@@ -60,9 +60,9 @@
     GlobalVar("gl_SampleID", ty.i32(),
               utils::Vector{
                   Builtin(ast::BuiltinValue::kSampleIndex),
-                  Disable(ast::DisabledValidation::kIgnoreStorageClass),
+                  Disable(ast::DisabledValidation::kIgnoreAddressSpace),
               },
-              ast::StorageClass::kIn);
+              ast::AddressSpace::kIn);
     Func("my_func", utils::Empty, ty.i32(),
          utils::Vector{
              Return(Expr("gl_SampleID")),
@@ -85,9 +85,9 @@
     GlobalVar("gl_SampleID", ty.i32(),
               utils::Vector{
                   Builtin(ast::BuiltinValue::kSampleIndex),
-                  Disable(ast::DisabledValidation::kIgnoreStorageClass),
+                  Disable(ast::DisabledValidation::kIgnoreAddressSpace),
               },
-              ast::StorageClass::kIn);
+              ast::AddressSpace::kIn);
     Func("my_func", utils::Empty, ty.i32(),
          utils::Vector{
              Return(Expr("gl_SampleID")),
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index c2d9109..f5733db 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -33,12 +33,12 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_Array) {
     auto* arr = ty.array<bool, 4>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, "ary"))
         << gen.error();
     EXPECT_EQ(out.str(), "bool ary[4]");
@@ -46,12 +46,12 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
     auto* arr = ty.array(ty.array<bool, 4>(), 5_u);
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, "ary"))
         << gen.error();
     EXPECT_EQ(out.str(), "bool ary[5][4]");
@@ -59,12 +59,12 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) {
     auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5_u), 6_u);
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, "ary"))
         << gen.error();
     EXPECT_EQ(out.str(), "bool ary[6][5][4]");
@@ -72,12 +72,12 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_Array_WithoutName) {
     auto* arr = ty.array<bool, 4>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "bool[4]");
@@ -89,7 +89,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, bool_, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, bool_, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "bool");
 }
@@ -100,7 +100,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, f32, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, f32, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "float");
 }
@@ -113,7 +113,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, f16, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, f16, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "float16_t");
 }
@@ -124,7 +124,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, i32, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, i32, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "int");
 }
@@ -137,7 +137,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "mat2x3");
 }
@@ -152,7 +152,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "f16mat2x3");
 }
@@ -162,7 +162,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -182,13 +182,13 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, sem_s, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, sem_s, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "S");
 }
@@ -198,7 +198,7 @@
                                  Member("double", ty.i32()),
                                  Member("float", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -215,7 +215,7 @@
                                  Member("a", ty.i32(), utils::Vector{MemberOffset(0_a)}),
                                  Member("b", ty.f32(), utils::Vector{MemberOffset(8_a)}),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -236,7 +236,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, u32, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, u32, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "uint");
 }
@@ -248,7 +248,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, vec3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, vec3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "vec3");
 }
@@ -262,7 +262,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, vec3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, vec3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "f16vec3");
 }
@@ -273,7 +273,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, void_, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, void_, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "void");
 }
@@ -284,7 +284,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_FALSE(gen.EmitType(out, sampler, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_FALSE(gen.EmitType(out, sampler, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
 }
 
@@ -294,7 +294,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_FALSE(gen.EmitType(out, sampler, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_FALSE(gen.EmitType(out, sampler, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
 }
 
@@ -500,7 +500,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, s, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "highp sampler2DMS");
 }
diff --git a/src/tint/writer/glsl/generator_impl_unary_op_test.cc b/src/tint/writer/glsl/generator_impl_unary_op_test.cc
index 009d173..ac54b4e 100644
--- a/src/tint/writer/glsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/glsl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using GlslUnaryOpTest = TestHelper;
 
 TEST_F(GlslUnaryOpTest, AddressOf) {
-    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Complement) {
-    GlobalVar("expr", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.u32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Indirection) {
-    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::AddressSpace::kPrivate);
     auto* p = Let("expr", create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
     WrapInFunction(p, op);
@@ -57,7 +57,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Not) {
-    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -69,7 +69,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Negation) {
-    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc b/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
index cae4735..35b1407 100644
--- a/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
@@ -26,7 +26,7 @@
 
 TEST_F(GlslGeneratorImplTest_UniformBuffer, Simple) {
     auto* simple = Structure("Simple", utils::Vector{Member("member", ty.f32())});
-    GlobalVar("simple", ty.Of(simple), ast::StorageClass::kUniform, Group(0_a), Binding(0_a));
+    GlobalVar("simple", ty.Of(simple), ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     GeneratorImpl& gen = Build();
 
@@ -46,7 +46,7 @@
 
 TEST_F(GlslGeneratorImplTest_UniformBuffer, Simple_Desktop) {
     auto* simple = Structure("Simple", utils::Vector{Member("member", ty.f32())});
-    GlobalVar("simple", ty.Of(simple), ast::StorageClass::kUniform, Group(0_a), Binding(0_a));
+    GlobalVar("simple", ty.Of(simple), ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index 6cac463..650e2b4 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -367,6 +367,75 @@
 )");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32_zero) {
+    auto* C = Const("C", Construct(ty.array<f32, 2>()));
+    Func("f", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(C),
+             Decl(Let("l", Expr(C))),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l[2] = float[2](0.0f, 0.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_arr_f32_zero) {
+    auto* C = Const("C", Construct(ty.array(ty.array<f32, 2>(), 3_i)));
+    Func("f", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(C),
+             Decl(Let("l", Expr(C))),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l[3][2] = float[3][2](float[2](0.0f, 0.0f), float[2](0.0f, 0.0f), float[2](0.0f, 0.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_struct_zero) {
+    Structure("S", utils::Vector{Member("a", ty.i32()), Member("b", ty.f32())});
+    auto* C = Const("C", Construct(ty.array(ty.type_name("S"), 2_i)));
+    Func("f", utils::Empty, ty.void_(),
+         utils::Vector{
+             Decl(C),
+             Decl(Let("l", Expr(C))),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+struct S {
+  int a;
+  float b;
+};
+
+void f() {
+  S l[2] = S[2](S(0, 0.0f), S(0, 0.0f));
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
     auto* C = Const("C", Construct(ty.array(ty.vec2<bool>(), 3_u),  //
                                    vec2<bool>(true, false),         //
@@ -406,7 +475,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Private) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     WrapInFunction(Expr("a"));
 
diff --git a/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc b/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
index 9b39fda..ef40708 100644
--- a/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
+++ b/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
@@ -27,7 +27,7 @@
 using GlslGeneratorImplTest_WorkgroupVar = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_WorkgroupVar, Basic) {
-    GlobalVar("wg", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.f32(), ast::AddressSpace::kWorkgroup);
 
     Func("main", utils::Empty, ty.void_(), utils::Vector{Assign("wg", 1.2_f)},
          utils::Vector{
@@ -43,7 +43,7 @@
 TEST_F(GlslGeneratorImplTest_WorkgroupVar, Aliased) {
     auto* alias = Alias("F32", ty.f32());
 
-    GlobalVar("wg", ty.Of(alias), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.Of(alias), ast::AddressSpace::kWorkgroup);
 
     Func("main", utils::Empty, ty.void_(), utils::Vector{Assign("wg", 1.2_f)},
          utils::Vector{
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 48090d3..34130d0 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -143,13 +143,6 @@
     return s;
 }
 
-const char* LoopAttribute() {
-    // Force loops not to be unrolled to work around FXC compilation issues when
-    // it attempts and fails to unroll loops when it contains gradient operations.
-    // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-while
-    return "[loop] ";
-}
-
 }  // namespace
 
 SanitizedResult::SanitizedResult() = default;
@@ -288,10 +281,10 @@
             },
             [&](const ast::Struct* str) {
                 auto* ty = builder_.Sem().Get(str);
-                auto storage_class_uses = ty->StorageClassUsage();
-                if (storage_class_uses.size() !=
-                    (storage_class_uses.count(ast::StorageClass::kStorage) +
-                     storage_class_uses.count(ast::StorageClass::kUniform))) {
+                auto address_space_uses = ty->AddressSpaceUsage();
+                if (address_space_uses.size() !=
+                    (address_space_uses.count(ast::AddressSpace::kStorage) +
+                     address_space_uses.count(ast::AddressSpace::kUniform))) {
                     // The structure is used as something other than a storage buffer or
                     // uniform buffer, so it needs to be emitted.
                     // Storage buffer are read and written to via a ByteAddressBuffer
@@ -332,7 +325,7 @@
         std::string fn;
         {
             std::ostringstream ss;
-            if (!EmitType(ss, vec, tint::ast::StorageClass::kInvalid, ast::Access::kUndefined,
+            if (!EmitType(ss, vec, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined,
                           "")) {
                 return "";
             }
@@ -341,12 +334,12 @@
         {
             auto out = line(&helpers_);
             out << "void " << fn << "(inout ";
-            if (!EmitTypeAndName(out, vec, ast::StorageClass::kInvalid, ast::Access::kUndefined,
+            if (!EmitTypeAndName(out, vec, ast::AddressSpace::kInvalid, ast::Access::kUndefined,
                                  "vec")) {
                 return "";
             }
             out << ", int idx, ";
-            if (!EmitTypeAndName(out, vec->type(), ast::StorageClass::kInvalid,
+            if (!EmitTypeAndName(out, vec->type(), ast::AddressSpace::kInvalid,
                                  ast::Access::kUndefined, "val")) {
                 return "";
             }
@@ -406,7 +399,7 @@
         std::string fn;
         {
             std::ostringstream ss;
-            if (!EmitType(ss, mat, tint::ast::StorageClass::kInvalid, ast::Access::kUndefined,
+            if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined,
                           "")) {
                 return "";
             }
@@ -415,12 +408,12 @@
         {
             auto out = line(&helpers_);
             out << "void " << fn << "(inout ";
-            if (!EmitTypeAndName(out, mat, ast::StorageClass::kInvalid, ast::Access::kUndefined,
+            if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kUndefined,
                                  "mat")) {
                 return "";
             }
             out << ", int col, ";
-            if (!EmitTypeAndName(out, mat->ColumnType(), ast::StorageClass::kInvalid,
+            if (!EmitTypeAndName(out, mat->ColumnType(), ast::AddressSpace::kInvalid,
                                  ast::Access::kUndefined, "val")) {
                 return "";
             }
@@ -475,7 +468,7 @@
         std::string fn;
         {
             std::ostringstream ss;
-            if (!EmitType(ss, mat, tint::ast::StorageClass::kInvalid, ast::Access::kUndefined,
+            if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined,
                           "")) {
                 return "";
             }
@@ -484,12 +477,12 @@
         {
             auto out = line(&helpers_);
             out << "void " << fn << "(inout ";
-            if (!EmitTypeAndName(out, mat, ast::StorageClass::kInvalid, ast::Access::kUndefined,
+            if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kUndefined,
                                  "mat")) {
                 return "";
             }
             out << ", int col, int row, ";
-            if (!EmitTypeAndName(out, mat->type(), ast::StorageClass::kInvalid,
+            if (!EmitTypeAndName(out, mat->type(), ast::AddressSpace::kInvalid,
                                  ast::Access::kUndefined, "val")) {
                 return "";
             }
@@ -594,7 +587,7 @@
     }
 
     out << "as";
-    if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     out << "(";
@@ -665,7 +658,7 @@
         if (auto* vec = ty->As<sem::Vector>()) {
             auto* elem_ty = vec->type();
 
-            if (!EmitType(out, ty, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, ty, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -730,7 +723,7 @@
         std::string ty_name;
         {
             std::ostringstream ss;
-            if (!EmitType(ss, ty, tint::ast::StorageClass::kInvalid, ast::Access::kUndefined, "")) {
+            if (!EmitType(ss, ty, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined, "")) {
                 return "";
             }
             ty_name = ss.str();
@@ -972,18 +965,18 @@
 
     if (auto* intrinsic = ast::GetAttribute<transform::DecomposeMemoryAccess::Intrinsic>(
             func->Declaration()->attributes)) {
-        switch (intrinsic->storage_class) {
-            case ast::StorageClass::kUniform:
+        switch (intrinsic->address_space) {
+            case ast::AddressSpace::kUniform:
                 return EmitUniformBufferAccess(out, expr, intrinsic);
-            case ast::StorageClass::kStorage:
+            case ast::AddressSpace::kStorage:
                 if (!intrinsic->IsAtomic()) {
                     return EmitStorageBufferAccess(out, expr, intrinsic);
                 }
                 break;
             default:
                 TINT_UNREACHABLE(Writer, diagnostics_)
-                    << "unsupported DecomposeMemoryAccess::Intrinsic storage class:"
-                    << intrinsic->storage_class;
+                    << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
+                    << intrinsic->address_space;
                 return false;
         }
     }
@@ -1088,7 +1081,7 @@
 bool GeneratorImpl::EmitTypeConversion(std::ostream& out,
                                        const sem::Call* call,
                                        const sem::TypeConversion* conv) {
-    if (!EmitType(out, conv->Target(), ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+    if (!EmitType(out, conv->Target(), ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
     out << "(";
@@ -1133,7 +1126,7 @@
     if (brackets) {
         out << "{";
     } else {
-        if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+        if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
             return false;
         }
         out << "(";
@@ -1438,12 +1431,12 @@
     auto rmw = [&](const char* hlsl) -> bool {
         {
             auto fn = line(&buf);
-            if (!EmitTypeAndName(fn, result_ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+            if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
                                  name)) {
                 return false;
             }
             fn << "(RWByteAddressBuffer buffer, uint offset, ";
-            if (!EmitTypeAndName(fn, result_ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+            if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
                                  "value")) {
                 return false;
             }
@@ -1459,7 +1452,7 @@
 
         {
             auto l = line(&buf);
-            if (!EmitTypeAndName(l, result_ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+            if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
                                  "original_value")) {
                 return false;
             }
@@ -1508,7 +1501,7 @@
             // InterlockedOr using 0 as the OR value
             {
                 auto fn = line(&buf);
-                if (!EmitTypeAndName(fn, result_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, name)) {
                     return false;
                 }
@@ -1524,7 +1517,7 @@
 
             {
                 auto l = line(&buf);
-                if (!EmitTypeAndName(l, result_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, "value")) {
                     return false;
                 }
@@ -1542,7 +1535,7 @@
             {
                 auto fn = line(&buf);
                 fn << "void " << name << "(RWByteAddressBuffer buffer, uint offset, ";
-                if (!EmitTypeAndName(fn, value_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, "value")) {
                     return false;
                 }
@@ -1558,7 +1551,7 @@
 
             {
                 auto l = line(&buf);
-                if (!EmitTypeAndName(l, value_ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+                if (!EmitTypeAndName(l, value_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
                                      "ignored")) {
                     return false;
                 }
@@ -1573,17 +1566,17 @@
             auto* value_ty = params[2]->Type()->UnwrapRef();
             {
                 auto fn = line(&buf);
-                if (!EmitTypeAndName(fn, result_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, name)) {
                     return false;
                 }
                 fn << "(RWByteAddressBuffer buffer, uint offset, ";
-                if (!EmitTypeAndName(fn, value_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, "compare")) {
                     return false;
                 }
                 fn << ", ";
-                if (!EmitTypeAndName(fn, value_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, "value")) {
                     return false;
                 }
@@ -1599,7 +1592,7 @@
 
             {  // T result = {0};
                 auto l = line(&buf);
-                if (!EmitTypeAndName(l, result_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, "result")) {
                     return false;
                 }
@@ -1634,7 +1627,7 @@
 
     if (!builtin->ReturnType()->Is<sem::Void>()) {
         auto pre = line();
-        if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::StorageClass::kNone,
+        if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
                              ast::Access::kUndefined, result)) {
             return false;
         }
@@ -1698,7 +1691,7 @@
             {  // T result = 0;
                 auto pre = line();
                 auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
-                if (!EmitTypeAndName(pre, value_ty, ast::StorageClass::kNone,
+                if (!EmitTypeAndName(pre, value_ty, ast::AddressSpace::kNone,
                                      ast::Access::kUndefined, result)) {
                     return false;
                 }
@@ -1739,7 +1732,7 @@
             {  // T compare_value = <compare_value>;
                 auto pre = line();
                 if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
-                                     ast::StorageClass::kNone, ast::Access::kUndefined, compare)) {
+                                     ast::AddressSpace::kNone, ast::Access::kUndefined, compare)) {
                     return false;
                 }
                 pre << " = ";
@@ -1848,7 +1841,7 @@
 
             {
                 auto l = line(b);
-                if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
+                if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
                               ast::Access::kUndefined, "")) {
                     return false;
                 }
@@ -1890,7 +1883,7 @@
             line(b) << member_type << " sig = frexp(" << in << ", exp);";
             {
                 auto l = line(b);
-                if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
+                if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
                               ast::Access::kUndefined, "")) {
                     return false;
                 }
@@ -2681,7 +2674,7 @@
     // Emit storage atomic helpers
     if (auto* intrinsic =
             ast::GetAttribute<transform::DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
-        if (intrinsic->storage_class == ast::StorageClass::kStorage && intrinsic->IsAtomic()) {
+        if (intrinsic->address_space == ast::AddressSpace::kStorage && intrinsic->IsAtomic()) {
             if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
                 return false;
             }
@@ -2703,14 +2696,14 @@
             auto typedef_name = UniqueIdentifier(name + "_ret");
             auto pre = line();
             pre << "typedef ";
-            if (!EmitTypeAndName(pre, sem->ReturnType(), ast::StorageClass::kNone,
+            if (!EmitTypeAndName(pre, sem->ReturnType(), ast::AddressSpace::kNone,
                                  ast::Access::kReadWrite, typedef_name)) {
                 return false;
             }
             pre << ";";
             out << typedef_name;
         } else {
-            if (!EmitType(out, sem->ReturnType(), ast::StorageClass::kNone, ast::Access::kReadWrite,
+            if (!EmitType(out, sem->ReturnType(), ast::AddressSpace::kNone, ast::Access::kReadWrite,
                           "")) {
                 return false;
             }
@@ -2727,19 +2720,19 @@
             first = false;
 
             auto const* type = v->Type();
-            auto storage_class = ast::StorageClass::kNone;
+            auto address_space = ast::AddressSpace::kNone;
             auto access = ast::Access::kUndefined;
 
             if (auto* ptr = type->As<sem::Pointer>()) {
                 type = ptr->StoreType();
-                switch (ptr->StorageClass()) {
-                    case ast::StorageClass::kStorage:
-                    case ast::StorageClass::kUniform:
+                switch (ptr->AddressSpace()) {
+                    case ast::AddressSpace::kStorage:
+                    case ast::AddressSpace::kUniform:
                         // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
                         // storage buffers and uniform buffers down into transform-generated
                         // functions. In this situation we want to generate the parameter without an
-                        // 'inout', using the storage class and access from the pointer.
-                        storage_class = ptr->StorageClass();
+                        // 'inout', using the address space and access from the pointer.
+                        address_space = ptr->AddressSpace();
                         access = ptr->Access();
                         break;
                     default:
@@ -2748,13 +2741,13 @@
                 }
             }
 
-            // Note: WGSL only allows for StorageClass::kNone on parameters, however
+            // Note: WGSL only allows for AddressSpace::kNone on parameters, however
             // the sanitizer transforms generates load / store functions for storage
             // or uniform buffers. These functions have a buffer parameter with
-            // StorageClass::kStorage or StorageClass::kUniform. This is required to
+            // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
             // correctly translate the parameter to a [RW]ByteAddressBuffer for
             // storage buffers and a uint4[N] for uniform buffers.
-            if (!EmitTypeAndName(out, type, storage_class, access,
+            if (!EmitTypeAndName(out, type, address_space, access,
                                  builder_.Symbols().NameFor(v->Declaration()->symbol))) {
                 return false;
             }
@@ -2801,7 +2794,7 @@
     auto name = builder_.Symbols().NameFor(builder_.Symbols().New("unused"));
     {
         auto out = line();
-        if (!EmitTypeAndName(out, sem->ReturnType(), ast::StorageClass::kNone,
+        if (!EmitTypeAndName(out, sem->ReturnType(), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, name)) {
             return false;
         }
@@ -2817,25 +2810,25 @@
         global,  //
         [&](const ast::Var* var) {
             auto* sem = builder_.Sem().Get(global);
-            switch (sem->StorageClass()) {
-                case ast::StorageClass::kUniform:
+            switch (sem->AddressSpace()) {
+                case ast::AddressSpace::kUniform:
                     return EmitUniformVariable(var, sem);
-                case ast::StorageClass::kStorage:
+                case ast::AddressSpace::kStorage:
                     return EmitStorageVariable(var, sem);
-                case ast::StorageClass::kHandle:
+                case ast::AddressSpace::kHandle:
                     return EmitHandleVariable(var, sem);
-                case ast::StorageClass::kPrivate:
+                case ast::AddressSpace::kPrivate:
                     return EmitPrivateVariable(sem);
-                case ast::StorageClass::kWorkgroup:
+                case ast::AddressSpace::kWorkgroup:
                     return EmitWorkgroupVariable(sem);
-                case ast::StorageClass::kPushConstant:
+                case ast::AddressSpace::kPushConstant:
                     diagnostics_.add_error(
                         diag::System::Writer,
-                        "unhandled storage class " + utils::ToString(sem->StorageClass()));
+                        "unhandled address space " + utils::ToString(sem->AddressSpace()));
                     return false;
                 default: {
                     TINT_ICE(Writer, diagnostics_)
-                        << "unhandled storage class " << sem->StorageClass();
+                        << "unhandled address space " << sem->AddressSpace();
                     return false;
                 }
             }
@@ -2867,7 +2860,7 @@
     {
         ScopedIndent si(this);
         auto out = line();
-        if (!EmitTypeAndName(out, type, ast::StorageClass::kUniform, sem->Access(), name)) {
+        if (!EmitTypeAndName(out, type, ast::AddressSpace::kUniform, sem->Access(), name)) {
             return false;
         }
         out << ";";
@@ -2881,7 +2874,7 @@
 bool GeneratorImpl::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
     auto* type = sem->Type()->UnwrapRef();
     auto out = line();
-    if (!EmitTypeAndName(out, type, ast::StorageClass::kStorage, sem->Access(),
+    if (!EmitTypeAndName(out, type, ast::AddressSpace::kStorage, sem->Access(),
                          builder_.Symbols().NameFor(var->symbol))) {
         return false;
     }
@@ -2900,7 +2893,7 @@
 
     auto name = builder_.Symbols().NameFor(var->symbol);
     auto* type = sem->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, sem->StorageClass(), sem->Access(), name)) {
+    if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
         return false;
     }
 
@@ -2932,7 +2925,7 @@
 
     auto name = builder_.Symbols().NameFor(decl->symbol);
     auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->StorageClass(), var->Access(), name)) {
+    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
         return false;
     }
 
@@ -2959,7 +2952,7 @@
 
     auto name = builder_.Symbols().NameFor(decl->symbol);
     auto* type = var->Type()->UnwrapRef();
-    if (!EmitTypeAndName(out, type, var->StorageClass(), var->Access(), name)) {
+    if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
         return false;
     }
 
@@ -3078,7 +3071,7 @@
             }
             first = false;
 
-            if (!EmitTypeAndName(out, type, sem->StorageClass(), sem->Access(),
+            if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
                                  builder_.Symbols().NameFor(var->symbol))) {
                 return false;
             }
@@ -3148,7 +3141,7 @@
                 return true;
             }
 
-            if (!EmitType(out, v, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -3165,7 +3158,7 @@
             return true;
         },
         [&](const sem::Matrix* m) {
-            if (!EmitType(out, m, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
@@ -3184,7 +3177,7 @@
         [&](const sem::Array* a) {
             if (constant->AllZero()) {
                 out << "(";
-                if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                     return false;
                 }
                 out << ")0";
@@ -3214,7 +3207,7 @@
         [&](const sem::Struct* s) {
             if (constant->AllZero()) {
                 out << "(";
-                if (!EmitType(out, s, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                     return false;
                 }
                 out << ")0";
@@ -3303,7 +3296,7 @@
             return true;
         },
         [&](const sem::Vector* vec) {
-            if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+            if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
                 return false;
             }
             ScopedParen sp(out);
@@ -3318,7 +3311,7 @@
             return true;
         },
         [&](const sem::Matrix* mat) {
-            if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
+            if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
                 return false;
             }
             ScopedParen sp(out);
@@ -3335,12 +3328,12 @@
         [&](const sem::Struct*) {
             out << "(";
             TINT_DEFER(out << ")" << value);
-            return EmitType(out, type, ast::StorageClass::kNone, ast::Access::kUndefined, "");
+            return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
         },
         [&](const sem::Array*) {
             out << "(";
             TINT_DEFER(out << ")" << value);
-            return EmitType(out, type, ast::StorageClass::kNone, ast::Access::kUndefined, "");
+            return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
         },
         [&](Default) {
             diagnostics_.add_error(
@@ -3365,7 +3358,7 @@
     };
 
     TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-    line() << LoopAttribute() << "while (true) {";
+    line() << "while (true) {";
     {
         ScopedIndent si(this);
         if (!EmitStatements(stmt->body->statements)) {
@@ -3435,7 +3428,7 @@
         };
 
         TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
-        line() << LoopAttribute() << "while (true) {";
+        line() << "while (true) {";
         increment_indent();
         TINT_DEFER({
             decrement_indent();
@@ -3458,7 +3451,7 @@
         // For-loop can be generated.
         {
             auto out = line();
-            out << LoopAttribute() << "for";
+            out << "for";
             {
                 ScopedParen sp(out);
 
@@ -3507,7 +3500,7 @@
     // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
     bool emit_as_loop = cond_pre.lines.size() > 0;
     if (emit_as_loop) {
-        line() << LoopAttribute() << "while (true) {";
+        line() << "while (true) {";
         increment_indent();
         TINT_DEFER({
             decrement_indent();
@@ -3523,7 +3516,7 @@
         // While can be generated.
         {
             auto out = line();
-            out << LoopAttribute() << "while";
+            out << "while";
             {
                 ScopedParen sp(out);
                 out << cond_buf.str();
@@ -3705,21 +3698,21 @@
 
 bool GeneratorImpl::EmitType(std::ostream& out,
                              const sem::Type* type,
-                             ast::StorageClass storage_class,
+                             ast::AddressSpace address_space,
                              ast::Access access,
                              const std::string& name,
                              bool* name_printed /* = nullptr */) {
     if (name_printed) {
         *name_printed = false;
     }
-    switch (storage_class) {
-        case ast::StorageClass::kStorage:
+    switch (address_space) {
+        case ast::AddressSpace::kStorage:
             if (access != ast::Access::kRead) {
                 out << "RW";
             }
             out << "ByteAddressBuffer";
             return true;
-        case ast::StorageClass::kUniform: {
+        case ast::AddressSpace::kUniform: {
             auto array_length = (type->Size() + 15) / 16;
             out << "uint4 " << name << "[" << array_length << "]";
             if (name_printed) {
@@ -3753,7 +3746,7 @@
                 sizes.push_back(count.value());
                 base_type = arr->ElemType();
             }
-            if (!EmitType(out, base_type, storage_class, access, "")) {
+            if (!EmitType(out, base_type, address_space, access, "")) {
                 return false;
             }
             if (!name.empty()) {
@@ -3787,13 +3780,13 @@
             if (mat->type()->Is<sem::F16>()) {
                 // Use matrix<type, N, M> for f16 matrix
                 out << "matrix<";
-                if (!EmitType(out, mat->type(), storage_class, access, "")) {
+                if (!EmitType(out, mat->type(), address_space, access, "")) {
                     return false;
                 }
                 out << ", " << mat->columns() << ", " << mat->rows() << ">";
                 return true;
             }
-            if (!EmitType(out, mat->type(), storage_class, access, "")) {
+            if (!EmitType(out, mat->type(), address_space, access, "")) {
                 return false;
             }
             // Note: HLSL's matrices are declared as <type>NxM, where N is the
@@ -3910,7 +3903,7 @@
             } else {
                 // For example, use "vector<float16_t, N>" for f16 vector.
                 out << "vector<";
-                if (!EmitType(out, vec->type(), storage_class, access, "")) {
+                if (!EmitType(out, vec->type(), address_space, access, "")) {
                     return false;
                 }
                 out << ", " << width << ">";
@@ -3918,7 +3911,7 @@
             return true;
         },
         [&](const sem::Atomic* atomic) {
-            return EmitType(out, atomic->Type(), storage_class, access, name);
+            return EmitType(out, atomic->Type(), address_space, access, name);
         },
         [&](const sem::Void*) {
             out << "void";
@@ -3932,11 +3925,11 @@
 
 bool GeneratorImpl::EmitTypeAndName(std::ostream& out,
                                     const sem::Type* type,
-                                    ast::StorageClass storage_class,
+                                    ast::AddressSpace address_space,
                                     ast::Access access,
                                     const std::string& name) {
     bool name_printed = false;
-    if (!EmitType(out, type, storage_class, access, name, &name_printed)) {
+    if (!EmitType(out, type, address_space, access, name, &name_printed)) {
         return false;
     }
     if (!name.empty() && !name_printed) {
@@ -4010,7 +4003,7 @@
             }
 
             out << pre;
-            if (!EmitTypeAndName(out, ty, ast::StorageClass::kNone, ast::Access::kReadWrite,
+            if (!EmitTypeAndName(out, ty, ast::AddressSpace::kNone, ast::Access::kReadWrite,
                                  mem_name)) {
                 return false;
             }
@@ -4061,7 +4054,7 @@
     auto* type = sem->Type()->UnwrapRef();
 
     auto out = line();
-    if (!EmitTypeAndName(out, type, sem->StorageClass(), sem->Access(),
+    if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
                          builder_.Symbols().NameFor(var->symbol))) {
         return false;
     }
@@ -4088,7 +4081,7 @@
 
     auto out = line();
     out << "const ";
-    if (!EmitTypeAndName(out, type, ast::StorageClass::kNone, ast::Access::kUndefined,
+    if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
                          builder_.Symbols().NameFor(let->symbol))) {
         return false;
     }
@@ -4115,7 +4108,7 @@
         std::vector<std::string> parameter_names;
         {
             auto decl = line(&b);
-            if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::StorageClass::kNone,
+            if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
                                  ast::Access::kUndefined, fn_name)) {
                 return "";
             }
@@ -4131,7 +4124,7 @@
                         decl << "inout ";
                         ty = ptr->StoreType();
                     }
-                    if (!EmitTypeAndName(decl, ty, ast::StorageClass::kNone,
+                    if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
                                          ast::Access::kUndefined, param_name)) {
                         return "";
                     }
diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h
index abbf818..0f6a668 100644
--- a/src/tint/writer/hlsl/generator_impl.h
+++ b/src/tint/writer/hlsl/generator_impl.h
@@ -303,30 +303,30 @@
     /// @returns true on success
     bool EmitGlobalVariable(const ast::Variable* global);
 
-    /// Handles emitting a global variable with the uniform storage class
+    /// Handles emitting a global variable with the uniform address space
     /// @param var the AST node for the 'var'
     /// @param sem the semantic node for the 'var'
     /// @returns true on success
     bool EmitUniformVariable(const ast::Var* var, const sem::Variable* sem);
 
-    /// Handles emitting a global variable with the storage storage class
+    /// Handles emitting a global variable with the storage address space
     /// @param var the AST node for the 'var'
     /// @param sem the semantic node for the 'var'
     /// @returns true on success
     bool EmitStorageVariable(const ast::Var* var, const sem::Variable* sem);
 
-    /// Handles emitting a global variable with the handle storage class
+    /// Handles emitting a global variable with the handle address space
     /// @param var the AST node for the 'var'
     /// @param sem the semantic node for the 'var'
     /// @returns true on success
     bool EmitHandleVariable(const ast::Var* var, const sem::Variable* sem);
 
-    /// Handles emitting a global variable with the private storage class
+    /// Handles emitting a global variable with the private address space
     /// @param var the global variable
     /// @returns true on success
     bool EmitPrivateVariable(const sem::Variable* var);
 
-    /// Handles emitting a global variable with the workgroup storage class
+    /// Handles emitting a global variable with the workgroup address space
     /// @param var the global variable
     /// @returns true on success
     bool EmitWorkgroupVariable(const sem::Variable* var);
@@ -390,7 +390,7 @@
     /// Handles generating type
     /// @param out the output stream
     /// @param type the type to generate
-    /// @param storage_class the storage class of the variable
+    /// @param address_space the address space of the variable
     /// @param access the access control type of the variable
     /// @param name the name of the variable, used for array emission.
     /// @param name_printed (optional) if not nullptr and an array was printed
@@ -398,20 +398,20 @@
     /// @returns true if the type is emitted
     bool EmitType(std::ostream& out,
                   const sem::Type* type,
-                  ast::StorageClass storage_class,
+                  ast::AddressSpace address_space,
                   ast::Access access,
                   const std::string& name,
                   bool* name_printed = nullptr);
     /// Handles generating type and name
     /// @param out the output stream
     /// @param type the type to generate
-    /// @param storage_class the storage class of the variable
+    /// @param address_space the address space of the variable
     /// @param access the access control type of the variable
     /// @param name the name to emit
     /// @returns true if the type is emitted
     bool EmitTypeAndName(std::ostream& out,
                          const sem::Type* type,
-                         ast::StorageClass storage_class,
+                         ast::AddressSpace address_space,
                          ast::Access access,
                          const std::string& name);
     /// Handles generating a structure declaration
diff --git a/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
index dfa3d67..0da678a 100644
--- a/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
@@ -22,7 +22,7 @@
 using HlslGeneratorImplTest_Expression = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Expression, IndexAccessor) {
-    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::AddressSpace::kPrivate);
     auto* expr = IndexAccessor("ary", 5_i);
     WrapInFunction(expr);
 
diff --git a/src/tint/writer/hlsl/generator_impl_binary_test.cc b/src/tint/writer/hlsl/generator_impl_binary_test.cc
index 8a87697..1163078 100644
--- a/src/tint/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_binary_test.cc
@@ -50,8 +50,8 @@
         return;
     }
 
-    GlobalVar("left", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -82,8 +82,8 @@
 
     Enable(ast::Extension::kF16);
 
-    GlobalVar("left", ty.f16(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.f16(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.f16(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -105,8 +105,8 @@
         return;
     }
 
-    GlobalVar("left", ty.u32(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.u32(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.u32(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -133,8 +133,8 @@
         return;
     }
 
-    GlobalVar("left", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("right", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -237,7 +237,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixScalar_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_f);
 
@@ -254,7 +254,7 @@
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixScalar_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_h);
 
@@ -269,7 +269,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarMatrix_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("mat");
 
@@ -286,7 +286,7 @@
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarMatrix_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr(1_h);
     auto* rhs = Expr("mat");
 
@@ -301,7 +301,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixVector_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = vec3<f32>(1_f, 1_f, 1_f);
 
@@ -318,7 +318,7 @@
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixVector_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = vec3<f16>(1_h, 1_h, 1_h);
 
@@ -333,7 +333,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_VectorMatrix_f32) {
-    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* lhs = vec3<f32>(1_f, 1_f, 1_f);
     auto* rhs = Expr("mat");
 
@@ -350,7 +350,7 @@
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_VectorMatrix_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("mat", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* lhs = vec3<f16>(1_h, 1_h, 1_h);
     auto* rhs = Expr("mat");
 
@@ -365,8 +365,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixMatrix_f32) {
-    GlobalVar("lhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -381,8 +381,8 @@
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixMatrix_f16) {
     Enable(ast::Extension::kF16);
 
-    GlobalVar("lhs", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -395,8 +395,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_And) {
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -415,10 +415,10 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_Multi) {
     // (a && b) || (c || d)
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -447,8 +447,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_Or) {
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -474,9 +474,9 @@
     //   return 3i;
     // }
 
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr =
         If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
@@ -511,9 +511,9 @@
 TEST_F(HlslGeneratorImplTest_Binary, Return_WithLogical) {
     // return (a && b) || c;
 
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr = Return(create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -539,10 +539,10 @@
 TEST_F(HlslGeneratorImplTest_Binary, Assign_WithLogical) {
     // a = (b || c) && d;
 
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* expr =
         Assign(Expr("a"),
@@ -570,12 +570,12 @@
 TEST_F(HlslGeneratorImplTest_Binary, Decl_WithLogical) {
     // var a : bool = (b && c) || d;
 
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* var =
-        Var("a", ty.bool_(), ast::StorageClass::kNone,
+        Var("a", ty.bool_(), ast::AddressSpace::kNone,
             create<ast::BinaryExpression>(
                 ast::BinaryOp::kLogicalOr,
                 create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("b"), Expr("c")),
@@ -609,10 +609,10 @@
              Param(Sym(), ty.bool_()),
          },
          ty.void_(), utils::Empty, utils::Empty);
-    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::AddressSpace::kPrivate);
 
     utils::Vector params{
         create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index 3f9cfa7..360c8f1 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -198,19 +198,19 @@
     if (param.type == CallParamType::kF16) {
         Enable(ast::Extension::kF16);
 
-        GlobalVar("h2", ty.vec2<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("h3", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("hm2x2", ty.mat2x2<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("hm3x2", ty.mat3x2<f16>(), ast::StorageClass::kPrivate);
+        GlobalVar("h2", ty.vec2<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("h3", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("hm2x2", ty.mat2x2<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("hm3x2", ty.mat3x2<f16>(), ast::AddressSpace::kPrivate);
     }
 
-    GlobalVar("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
-    GlobalVar("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f2", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("f3", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("u2", ty.vec2<u32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("i2", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b2", ty.vec2<bool>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = GenerateCall(param.builtin, param.type, this);
     ASSERT_NE(nullptr, call) << "Unhandled builtin";
@@ -342,8 +342,8 @@
 TEST_F(HlslGeneratorImplTest_Builtin, Builtin_Call) {
     auto* call = Call("dot", "param1", "param2");
 
-    GlobalVar("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     WrapInFunction(CallStmt(call));
 
@@ -356,25 +356,29 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Builtin, Select_Scalar) {
-    auto* call = Call("select", 1_f, 2_f, true);
+    GlobalVar("a", Expr(1_f), ast::AddressSpace::kPrivate);
+    GlobalVar("b", Expr(2_f), ast::AddressSpace::kPrivate);
+    auto* call = Call("select", "a", "b", true);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
     std::stringstream out;
     ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
-    EXPECT_EQ(out.str(), "(true ? 2.0f : 1.0f)");
+    EXPECT_EQ(out.str(), "(true ? b : a)");
 }
 
 TEST_F(HlslGeneratorImplTest_Builtin, Select_Vector) {
-    auto* call = Call("select", vec2<i32>(1_i, 2_i), vec2<i32>(3_i, 4_i), vec2<bool>(true, false));
+    GlobalVar("a", vec2<i32>(1_i, 2_i), ast::AddressSpace::kPrivate);
+    GlobalVar("b", vec2<i32>(3_i, 4_i), ast::AddressSpace::kPrivate);
+    auto* call = Call("select", "a", "b", vec2<bool>(true, false));
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
     gen.increment_indent();
     std::stringstream out;
     ASSERT_TRUE(gen.EmitExpression(out, call)) << gen.error();
-    EXPECT_EQ(out.str(), "(bool2(true, false) ? int2(3, 4) : int2(1, 2))");
+    EXPECT_EQ(out.str(), "(bool2(true, false) ? b : a)");
 }
 
 TEST_F(HlslGeneratorImplTest_Builtin, Modf_Scalar_f32) {
@@ -767,7 +771,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack4x8Snorm) {
     auto* call = Call("pack4x8snorm", "p1");
-    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -789,7 +793,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack4x8Unorm) {
     auto* call = Call("pack4x8unorm", "p1");
-    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -811,7 +815,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack2x16Snorm) {
     auto* call = Call("pack2x16snorm", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -833,7 +837,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack2x16Unorm) {
     auto* call = Call("pack2x16unorm", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -855,7 +859,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack2x16Float) {
     auto* call = Call("pack2x16float", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -877,7 +881,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack4x8Snorm) {
     auto* call = Call("unpack4x8snorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -900,7 +904,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack4x8Unorm) {
     auto* call = Call("unpack4x8unorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -923,7 +927,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack2x16Snorm) {
     auto* call = Call("unpack2x16snorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -946,7 +950,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack2x16Unorm) {
     auto* call = Call("unpack2x16unorm", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -969,7 +973,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack2x16Float) {
     auto* call = Call("unpack2x16float", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/hlsl/generator_impl_call_test.cc b/src/tint/writer/hlsl/generator_impl_call_test.cc
index 82fb926..33e0762 100644
--- a/src/tint/writer/hlsl/generator_impl_call_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_call_test.cc
@@ -42,8 +42,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.f32(), utils::Vector{Return(1.23_f)});
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -62,8 +62,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), utils::Empty, utils::Empty);
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = CallStmt(Call("my_func", "param1", "param2"));
     WrapInFunction(call);
diff --git a/src/tint/writer/hlsl/generator_impl_continue_test.cc b/src/tint/writer/hlsl/generator_impl_continue_test.cc
index c7192f6..6ba80c0 100644
--- a/src/tint/writer/hlsl/generator_impl_continue_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_continue_test.cc
@@ -29,7 +29,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(loop)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while (true) {
+    EXPECT_EQ(gen.result(), R"(  while (true) {
     if (false) {
       break;
     }
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index 322b560..1adb852 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -101,7 +101,7 @@
     // fn f(foo : ptr<function, f32>) -> f32 {
     //   return *foo;
     // }
-    Func("f", utils::Vector{Param("foo", ty.pointer<f32>(ast::StorageClass::kFunction))}, ty.f32(),
+    Func("f", utils::Vector{Param("foo", ty.pointer<f32>(ast::AddressSpace::kFunction))}, ty.f32(),
          utils::Vector{Return(Deref("foo"))});
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -361,7 +361,7 @@
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
     auto* ubo_ty = Structure("UBO", utils::Vector{Member("coord", ty.vec4<f32>())});
     auto* ubo =
-        GlobalVar("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform, Binding(0_a), Group(1_a));
+        GlobalVar("ubo", ty.Of(ubo_ty), ast::AddressSpace::kUniform, Binding(0_a), Group(1_a));
 
     Func("sub_func",
          utils::Vector{
@@ -404,7 +404,7 @@
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_UniformStruct) {
     auto* s = Structure("Uniforms", utils::Vector{Member("coord", ty.vec4<f32>())});
 
-    GlobalVar("uniforms", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a), Group(1_a));
+    GlobalVar("uniforms", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(1_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
 
@@ -437,7 +437,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
@@ -470,7 +470,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(1_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
@@ -503,7 +503,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     Func("frag_main", utils::Empty, ty.void_(),
@@ -534,7 +534,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     Func("frag_main", utils::Empty, ty.void_(),
@@ -561,7 +561,7 @@
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
     auto* s = Structure("S", utils::Vector{Member("x", ty.f32())});
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kUniform, Binding(0_a), Group(1_a));
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a), Group(1_a));
 
     Func("sub_func",
          utils::Vector{
@@ -603,7 +603,7 @@
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
     auto* s = Structure("S", utils::Vector{Member("x", ty.f32())});
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(1_a));
 
     Func("sub_func",
@@ -831,7 +831,7 @@
 
     auto* s = Structure("Data", utils::Vector{Member("d", ty.f32())});
 
-    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("data", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(0_a));
 
     {
diff --git a/src/tint/writer/hlsl/generator_impl_identifier_test.cc b/src/tint/writer/hlsl/generator_impl_identifier_test.cc
index d4b645f..b6434d8 100644
--- a/src/tint/writer/hlsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_identifier_test.cc
@@ -20,7 +20,7 @@
 using HlslGeneratorImplTest_Identifier = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Identifier, EmitIdentifierExpression) {
-    GlobalVar("foo", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("foo", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* i = Expr("foo");
     WrapInFunction(i);
diff --git a/src/tint/writer/hlsl/generator_impl_if_test.cc b/src/tint/writer/hlsl/generator_impl_if_test.cc
index 3195a06..37c9ea9 100644
--- a/src/tint/writer/hlsl/generator_impl_if_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_if_test.cc
@@ -20,7 +20,7 @@
 using HlslGeneratorImplTest_If = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_If, Emit_If) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* cond = Expr("cond");
     auto* body = Block(Return());
@@ -38,8 +38,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElseIf) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_cond = Expr("else_cond");
     auto* else_body = Block(Return());
@@ -65,7 +65,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElse) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_body = Block(Return());
 
@@ -88,8 +88,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithMultiple) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_cond = Expr("else_cond");
 
diff --git a/src/tint/writer/hlsl/generator_impl_import_test.cc b/src/tint/writer/hlsl/generator_impl_import_test.cc
index d6fdeac..14094ad 100644
--- a/src/tint/writer/hlsl/generator_impl_import_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_import_test.cc
@@ -237,10 +237,6 @@
                                          HlslImportData{"clamp", "clamp"},
                                          HlslImportData{"smoothstep", "smoothstep"}));
 
-TEST_F(HlslGeneratorImplTest_Import, DISABLED_HlslImportData_FMix) {
-    FAIL();
-}
-
 using HlslImportData_TripleParam_Int_Test = TestParamHelper<HlslImportData>;
 TEST_P(HlslImportData_TripleParam_Int_Test, IntScalar) {
     auto param = GetParam();
@@ -259,7 +255,7 @@
                          testing::Values(HlslImportData{"clamp", "clamp"}));
 
 TEST_F(HlslGeneratorImplTest_Import, HlslImportData_Determinant) {
-    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("determinant", "var");
     WrapInFunction(expr);
diff --git a/src/tint/writer/hlsl/generator_impl_loop_test.cc b/src/tint/writer/hlsl/generator_impl_loop_test.cc
index 77f0dd4..92f966c 100644
--- a/src/tint/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_loop_test.cc
@@ -34,7 +34,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while (true) {
+    EXPECT_EQ(gen.result(), R"(  while (true) {
     discard;
   }
 )");
@@ -54,7 +54,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while (true) {
+    EXPECT_EQ(gen.result(), R"(  while (true) {
     discard;
     {
       a_statement();
@@ -66,8 +66,8 @@
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    GlobalVar("lhs", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* body = Block(create<ast::DiscardStatement>());
     auto* continuing = Block(CallStmt(Call("a_statement")));
@@ -88,8 +88,8 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while (true) {
-    [loop] while (true) {
+    EXPECT_EQ(gen.result(), R"(  while (true) {
+    while (true) {
       discard;
       {
         a_statement();
@@ -112,7 +112,7 @@
     //   }
     // }
 
-    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.4_f))),  //
                        Decl(Var("other", ty.f32())),             //
@@ -127,7 +127,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while (true) {
+    EXPECT_EQ(gen.result(), R"(  while (true) {
     float lhs = 2.400000095f;
     float other = 0.0f;
     break;
@@ -152,7 +152,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] for(; ; ) {
+    for(; ; ) {
       return;
     }
   }
@@ -173,7 +173,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] for(int i = 0; ; ) {
+    for(int i = 0; ; ) {
       return;
     }
   }
@@ -201,7 +201,7 @@
       tint_tmp = false;
     }
     bool b = (tint_tmp);
-    [loop] for(; ; ) {
+    for(; ; ) {
       return;
     }
   }
@@ -222,7 +222,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] for(; true; ) {
+    for(; true; ) {
       return;
     }
   }
@@ -245,7 +245,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] while (true) {
+    while (true) {
       bool tint_tmp = true;
       if (tint_tmp) {
         tint_tmp = false;
@@ -272,7 +272,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] for(; ; i = (i + 1)) {
+    for(; ; i = (i + 1)) {
       return;
     }
   }
@@ -296,7 +296,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] while (true) {
+    while (true) {
       return;
       bool tint_tmp = true;
       if (tint_tmp) {
@@ -322,7 +322,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  {
-    [loop] for(int i = 0; true; i = (i + 1)) {
+    for(int i = 0; true; i = (i + 1)) {
       return;
     }
   }
@@ -356,7 +356,7 @@
       tint_tmp = false;
     }
     bool i = (tint_tmp);
-    [loop] while (true) {
+    while (true) {
       bool tint_tmp_1 = true;
       if (tint_tmp_1) {
         tint_tmp_1 = false;
@@ -386,7 +386,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while(true) {
+    EXPECT_EQ(gen.result(), R"(  while(true) {
     return;
   }
 )");
@@ -405,7 +405,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while(true) {
+    EXPECT_EQ(gen.result(), R"(  while(true) {
     continue;
   }
 )");
@@ -426,7 +426,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.error();
-    EXPECT_EQ(gen.result(), R"(  [loop] while (true) {
+    EXPECT_EQ(gen.result(), R"(  while (true) {
     bool tint_tmp = true;
     if (tint_tmp) {
       tint_tmp = false;
diff --git a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
index 9ca2d18..1fbef9b 100644
--- a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -90,7 +90,7 @@
         ProgramBuilder& b = *this;
         auto* s = b.Structure("Data", members);
 
-        b.GlobalVar("data", b.ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+        b.GlobalVar("data", b.ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
                     b.Group(1_a), b.Binding(0_a));
     }
 
@@ -111,7 +111,7 @@
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
     auto* s = Structure("Data", utils::Vector{Member("mem", ty.f32())});
-    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::AddressSpace::kPrivate);
 
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(Var("expr", ty.f32(), expr));
diff --git a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
index b9ebd87..dbe8a51 100644
--- a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -26,7 +26,7 @@
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -60,7 +60,7 @@
                                          Member(0, "z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -92,7 +92,7 @@
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     auto* p = Let("p", AddressOf("b"));
@@ -129,9 +129,9 @@
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
-    GlobalVar("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(2_a),
+    GlobalVar("c", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(2_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -239,7 +239,7 @@
     // let p : ptr<function, i32> = &v;
     // let x : i32 = *p;
     auto* v = Var("v", ty.i32());
-    auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(v));
+    auto* p = Let("p", ty.pointer<i32>(ast::AddressSpace::kFunction), AddressOf(v));
     auto* x = Var("x", ty.i32(), Deref(p));
 
     Func("main", utils::Empty, ty.void_(),
@@ -273,11 +273,11 @@
     // let vp : ptr<function, vec4<f32>> = &(*mp)[2i];
     // let v : vec4<f32> = *vp;
     auto* a = Var("a", ty.array(ty.mat4x4<f32>(), 4_u));
-    auto* ap = Let("ap", ty.pointer(ty.array(ty.mat4x4<f32>(), 4_u), ast::StorageClass::kFunction),
+    auto* ap = Let("ap", ty.pointer(ty.array(ty.mat4x4<f32>(), 4_u), ast::AddressSpace::kFunction),
                    AddressOf(a));
-    auto* mp = Let("mp", ty.pointer(ty.mat4x4<f32>(), ast::StorageClass::kFunction),
+    auto* mp = Let("mp", ty.pointer(ty.mat4x4<f32>(), ast::AddressSpace::kFunction),
                    AddressOf(IndexAccessor(Deref(ap), 3_i)));
-    auto* vp = Let("vp", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction),
+    auto* vp = Let("vp", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction),
                    AddressOf(IndexAccessor(Deref(mp), 2_i)));
     auto* v = Var("v", ty.vec4<f32>(), Deref(vp));
 
diff --git a/src/tint/writer/hlsl/generator_impl_switch_test.cc b/src/tint/writer/hlsl/generator_impl_switch_test.cc
index 7d89672..24c17a6 100644
--- a/src/tint/writer/hlsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_switch_test.cc
@@ -22,7 +22,7 @@
 using HlslGeneratorImplTest_Switch = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch) {
-    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
     auto* s = Switch(                     //
         Expr("cond"),                     //
         Case(Expr(5_i), Block(Break())),  //
@@ -46,8 +46,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch_OnlyDefaultCase) {
-    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
-    GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
+    GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
     auto* s = Switch(  //
         Expr("cond"),  //
         DefaultCase(Block(Assign(Expr("a"), Expr(42_i)))));
diff --git a/src/tint/writer/hlsl/generator_impl_type_test.cc b/src/tint/writer/hlsl/generator_impl_type_test.cc
index 75dac72..7e50180 100644
--- a/src/tint/writer/hlsl/generator_impl_type_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_type_test.cc
@@ -33,12 +33,12 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array) {
     auto* arr = ty.array<bool, 4>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, "ary"))
         << gen.error();
     EXPECT_EQ(out.str(), "bool ary[4]");
@@ -46,12 +46,12 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
     auto* arr = ty.array(ty.array<bool, 4>(), 5_u);
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, "ary"))
         << gen.error();
     EXPECT_EQ(out.str(), "bool ary[5][4]");
@@ -59,12 +59,12 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) {
     auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5_u), 6_u);
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, "ary"))
         << gen.error();
     EXPECT_EQ(out.str(), "bool ary[6][5][4]");
@@ -72,12 +72,12 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array_WithoutName) {
     auto* arr = ty.array<bool, 4>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone,
+    ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), ast::AddressSpace::kNone,
                              ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "bool[4]");
@@ -89,7 +89,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, bool_, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, bool_, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "bool");
 }
@@ -100,7 +100,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, f16, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, f16, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "float16_t");
 }
@@ -111,7 +111,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, f32, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, f32, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "float");
 }
@@ -122,7 +122,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, i32, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, i32, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "int");
 }
@@ -135,7 +135,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "matrix<float16_t, 2, 3>");
 }
@@ -148,7 +148,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "float2x3");
 }
@@ -158,7 +158,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -177,7 +177,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -191,13 +191,13 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, sem_s, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, sem_s, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "S");
 }
@@ -207,7 +207,7 @@
                                  Member("double", ty.i32()),
                                  Member("float", ty.f32()),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -224,7 +224,7 @@
                                  Member("a", ty.i32(), utils::Vector{MemberOffset(0_a)}),
                                  Member("b", ty.f32(), utils::Vector{MemberOffset(8_a)}),
                              });
-    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -244,7 +244,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, u32, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, u32, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "uint");
 }
@@ -256,7 +256,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, vec3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, vec3, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "float3");
 }
@@ -267,7 +267,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, void_, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, void_, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "void");
 }
@@ -278,7 +278,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, sampler, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, sampler, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "SamplerState");
 }
@@ -289,7 +289,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, sampler, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, sampler, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "SamplerComparisonState");
 }
@@ -499,7 +499,7 @@
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
-    ASSERT_TRUE(gen.EmitType(out, s, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+    ASSERT_TRUE(gen.EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kReadWrite, ""))
         << gen.error();
     EXPECT_EQ(out.str(), "Texture2DMS<float4>");
 }
diff --git a/src/tint/writer/hlsl/generator_impl_unary_op_test.cc b/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
index d311061..c7f0336 100644
--- a/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using HlslUnaryOpTest = TestHelper;
 
 TEST_F(HlslUnaryOpTest, AddressOf) {
-    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Complement) {
-    GlobalVar("expr", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.u32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Indirection) {
-    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::AddressSpace::kPrivate);
     auto* p = Let("expr", create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
     WrapInFunction(p, op);
@@ -57,7 +57,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Not) {
-    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -69,7 +69,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Negation) {
-    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index 4e4643f..5df476a 100644
--- a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -357,7 +357,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Private) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     WrapInFunction(Expr("a"));
 
diff --git a/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc b/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
index 9e8be01..4ec7af2 100644
--- a/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
@@ -27,7 +27,7 @@
 using HlslGeneratorImplTest_WorkgroupVar = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_WorkgroupVar, Basic) {
-    GlobalVar("wg", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.f32(), ast::AddressSpace::kWorkgroup);
 
     Func("main", utils::Empty, ty.void_(), utils::Vector{Assign("wg", 1.2_f)},
          utils::Vector{
@@ -43,7 +43,7 @@
 TEST_F(HlslGeneratorImplTest_WorkgroupVar, Aliased) {
     auto* alias = Alias("F32", ty.f32());
 
-    GlobalVar("wg", ty.Of(alias), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.Of(alias), ast::AddressSpace::kWorkgroup);
 
     Func("main", utils::Empty, ty.void_(), utils::Vector{Assign("wg", 1.2_f)},
          utils::Vector{
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 9d07aa8..f774315 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -193,7 +193,7 @@
         // Use the SSBO binding numbers as the indices for the buffer size lookups.
         for (auto* var : in->AST().GlobalVariables()) {
             auto* global = in->Sem().Get<sem::GlobalVariable>(var);
-            if (global && global->StorageClass() == ast::StorageClass::kStorage) {
+            if (global && global->AddressSpace() == ast::AddressSpace::kStorage) {
                 array_length_from_uniform_cfg.bindpoint_to_size_index.emplace(
                     global->BindingPoint(), global->BindingPoint().binding);
             }
@@ -517,8 +517,18 @@
         ScopedParen sp(out);
         {
             ScopedBitCast lhs_uint_cast(this, out, lhs_type, unsigned_type_of(target_type));
-            if (!EmitExpression(out, expr->lhs)) {
-                return false;
+
+            // In case the type is packed, cast to our own type in order to remove the packing.
+            // Otherwise, this just casts to itself.
+            if (lhs_type->is_signed_integer_vector()) {
+                ScopedBitCast lhs_self_cast(this, out, lhs_type, lhs_type);
+                if (!EmitExpression(out, expr->lhs)) {
+                    return false;
+                }
+            } else {
+                if (!EmitExpression(out, expr->lhs)) {
+                    return false;
+                }
             }
         }
         if (!emit_op()) {
@@ -526,8 +536,18 @@
         }
         {
             ScopedBitCast rhs_uint_cast(this, out, rhs_type, unsigned_type_of(target_type));
-            if (!EmitExpression(out, expr->rhs)) {
-                return false;
+
+            // In case the type is packed, cast to our own type in order to remove the packing.
+            // Otherwise, this just casts to itself.
+            if (rhs_type->is_signed_integer_vector()) {
+                ScopedBitCast rhs_self_cast(this, out, rhs_type, rhs_type);
+                if (!EmitExpression(out, expr->rhs)) {
+                    return false;
+                }
+            } else {
+                if (!EmitExpression(out, expr->rhs)) {
+                    return false;
+                }
             }
         }
         return true;
@@ -860,7 +880,7 @@
 
         case sem::BuiltinType::kAtomicCompareExchangeWeak: {
             auto* ptr_ty = TypeOf(expr->args[0])->UnwrapRef()->As<sem::Pointer>();
-            auto sc = ptr_ty->StorageClass();
+            auto sc = ptr_ty->AddressSpace();
             auto* str = builtin->ReturnType()->As<sem::Struct>();
 
             auto func = utils::GetOrCreate(
@@ -1982,12 +2002,12 @@
                     return false;
                 }
             } else if (auto* ptr = param->type->As<ast::Pointer>()) {
-                auto sc = ptr->storage_class;
-                if (sc == ast::StorageClass::kWorkgroup) {
+                auto sc = ptr->address_space;
+                if (sc == ast::AddressSpace::kWorkgroup) {
                     auto& allocations = workgroup_allocations_[func_name];
                     out << " [[threadgroup(" << allocations.size() << ")]]";
                     allocations.push_back(program_->Sem().Get(ptr->type)->Size());
-                } else if (sc == ast::StorageClass::kStorage || sc == ast::StorageClass::kUniform) {
+                } else if (sc == ast::AddressSpace::kStorage || sc == ast::AddressSpace::kUniform) {
                     uint32_t binding = get_binding_index(param);
                     if (binding == kInvalidBindingIndex) {
                         return false;
@@ -1995,7 +2015,7 @@
                     out << " [[buffer(" << binding << ")]]";
                 } else {
                     TINT_ICE(Writer, diagnostics_)
-                        << "invalid pointer storage class for entry point parameter";
+                        << "invalid pointer address space for entry point parameter";
                     return false;
                 }
             } else {
@@ -2531,7 +2551,7 @@
             if (ptr->Access() == ast::Access::kRead) {
                 out << "const ";
             }
-            if (!EmitStorageClass(out, ptr->StorageClass())) {
+            if (!EmitAddressSpace(out, ptr->AddressSpace())) {
                 return false;
             }
             out << " ";
@@ -2678,26 +2698,26 @@
     return true;
 }
 
-bool GeneratorImpl::EmitStorageClass(std::ostream& out, ast::StorageClass sc) {
+bool GeneratorImpl::EmitAddressSpace(std::ostream& out, ast::AddressSpace sc) {
     switch (sc) {
-        case ast::StorageClass::kFunction:
-        case ast::StorageClass::kPrivate:
-        case ast::StorageClass::kHandle:
+        case ast::AddressSpace::kFunction:
+        case ast::AddressSpace::kPrivate:
+        case ast::AddressSpace::kHandle:
             out << "thread";
             return true;
-        case ast::StorageClass::kWorkgroup:
+        case ast::AddressSpace::kWorkgroup:
             out << "threadgroup";
             return true;
-        case ast::StorageClass::kStorage:
+        case ast::AddressSpace::kStorage:
             out << "device";
             return true;
-        case ast::StorageClass::kUniform:
+        case ast::AddressSpace::kUniform:
             out << "constant";
             return true;
         default:
             break;
     }
-    TINT_ICE(Writer, diagnostics_) << "unhandled storage class: " << sc;
+    TINT_ICE(Writer, diagnostics_) << "unhandled address space: " << sc;
     return false;
 }
 
@@ -2978,19 +2998,19 @@
 
     auto out = line();
 
-    switch (sem->StorageClass()) {
-        case ast::StorageClass::kFunction:
-        case ast::StorageClass::kHandle:
-        case ast::StorageClass::kNone:
+    switch (sem->AddressSpace()) {
+        case ast::AddressSpace::kFunction:
+        case ast::AddressSpace::kHandle:
+        case ast::AddressSpace::kNone:
             break;
-        case ast::StorageClass::kPrivate:
+        case ast::AddressSpace::kPrivate:
             out << "thread ";
             break;
-        case ast::StorageClass::kWorkgroup:
+        case ast::AddressSpace::kWorkgroup:
             out << "threadgroup ";
             break;
         default:
-            TINT_ICE(Writer, diagnostics_) << "unhandled variable storage class";
+            TINT_ICE(Writer, diagnostics_) << "unhandled variable address space";
             return false;
     }
 
@@ -3008,9 +3028,9 @@
         if (!EmitExpression(out, var->constructor)) {
             return false;
         }
-    } else if (sem->StorageClass() == ast::StorageClass::kPrivate ||
-               sem->StorageClass() == ast::StorageClass::kFunction ||
-               sem->StorageClass() == ast::StorageClass::kNone) {
+    } else if (sem->AddressSpace() == ast::AddressSpace::kPrivate ||
+               sem->AddressSpace() == ast::AddressSpace::kFunction ||
+               sem->AddressSpace() == ast::AddressSpace::kNone) {
         out << " = ";
         if (!EmitZeroValue(out, type)) {
             return false;
@@ -3027,19 +3047,19 @@
 
     auto out = line();
 
-    switch (sem->StorageClass()) {
-        case ast::StorageClass::kFunction:
-        case ast::StorageClass::kHandle:
-        case ast::StorageClass::kNone:
+    switch (sem->AddressSpace()) {
+        case ast::AddressSpace::kFunction:
+        case ast::AddressSpace::kHandle:
+        case ast::AddressSpace::kNone:
             break;
-        case ast::StorageClass::kPrivate:
+        case ast::AddressSpace::kPrivate:
             out << "thread ";
             break;
-        case ast::StorageClass::kWorkgroup:
+        case ast::AddressSpace::kWorkgroup:
             out << "threadgroup ";
             break;
         default:
-            TINT_ICE(Writer, diagnostics_) << "unhandled variable storage class";
+            TINT_ICE(Writer, diagnostics_) << "unhandled variable address space";
             return false;
     }
 
diff --git a/src/tint/writer/msl/generator_impl.h b/src/tint/writer/msl/generator_impl.h
index a5948aa..7cd1b6d 100644
--- a/src/tint/writer/msl/generator_impl.h
+++ b/src/tint/writer/msl/generator_impl.h
@@ -319,11 +319,11 @@
     /// @param name the name to emit
     /// @returns true if the type is emitted
     bool EmitTypeAndName(std::ostream& out, const sem::Type* type, const std::string& name);
-    /// Handles generating a storage class
+    /// Handles generating a address space
     /// @param out the output of the type stream
-    /// @param sc the storage class to generate
-    /// @returns true if the storage class is emitted
-    bool EmitStorageClass(std::ostream& out, ast::StorageClass sc);
+    /// @param sc the address space to generate
+    /// @returns true if the address space is emitted
+    bool EmitAddressSpace(std::ostream& out, ast::AddressSpace sc);
     /// Handles generating an MSL-packed storage type.
     /// If the type does not have a packed form, the standard non-packed form is
     /// emitted.
@@ -420,7 +420,7 @@
     /// Name of atomicCompareExchangeWeak() helper for the given pointer storage
     /// class and struct return type
     using ACEWKeyType =
-        utils::UnorderedKeyWrapper<std::tuple<ast::StorageClass, const sem::Struct*>>;
+        utils::UnorderedKeyWrapper<std::tuple<ast::AddressSpace, const sem::Struct*>>;
     std::unordered_map<ACEWKeyType, std::string> atomicCompareExchangeWeak_;
 
     /// Unique name of the 'TINT_INVARIANT' preprocessor define.
diff --git a/src/tint/writer/msl/generator_impl_array_accessor_test.cc b/src/tint/writer/msl/generator_impl_array_accessor_test.cc
index e058ff5..771d537 100644
--- a/src/tint/writer/msl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/msl/generator_impl_array_accessor_test.cc
@@ -34,7 +34,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, IndexAccessor_OfDref) {
-    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::AddressSpace::kPrivate);
 
     auto* p = Let("p", AddressOf("ary"));
     auto* expr = IndexAccessor(Deref("p"), 5_i);
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index 9f75691..1770d5d 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -216,21 +216,21 @@
     if (param.type == CallParamType::kF16) {
         Enable(ast::Extension::kF16);
 
-        GlobalVar("h2", ty.vec2<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("h3", ty.vec3<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("hm2x2", ty.mat2x2<f16>(), ast::StorageClass::kPrivate);
-        GlobalVar("hm3x2", ty.mat3x2<f16>(), ast::StorageClass::kPrivate);
+        GlobalVar("h2", ty.vec2<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("h3", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("hm2x2", ty.mat2x2<f16>(), ast::AddressSpace::kPrivate);
+        GlobalVar("hm3x2", ty.mat3x2<f16>(), ast::AddressSpace::kPrivate);
     }
 
-    GlobalVar("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("f4", ty.vec4<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("u1", ty.u32(), ast::StorageClass::kPrivate);
-    GlobalVar("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
-    GlobalVar("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    GlobalVar("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f2", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("f3", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("f4", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("u1", ty.u32(), ast::AddressSpace::kPrivate);
+    GlobalVar("u2", ty.vec2<u32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("i2", ty.vec2<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("b2", ty.vec2<bool>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = GenerateCall(param.builtin, param.type, this);
     ASSERT_NE(nullptr, call) << "Unhandled builtin";
@@ -370,8 +370,8 @@
                     "unpack_unorm2x16_to_float"}));
 
 TEST_F(MslGeneratorImplTest, Builtin_Call) {
-    GlobalVar("param1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("dot", "param1", "param2");
     WrapInFunction(CallStmt(call));
@@ -855,7 +855,7 @@
 
 TEST_F(MslGeneratorImplTest, Pack2x16Float) {
     auto* call = Call("pack2x16float", "p1");
-    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
 
     GeneratorImpl& gen = Build();
@@ -867,7 +867,7 @@
 
 TEST_F(MslGeneratorImplTest, Unpack2x16Float) {
     auto* call = Call("unpack2x16float", "p1");
-    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(call));
 
     GeneratorImpl& gen = Build();
@@ -878,7 +878,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, DotI32) {
-    GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
     WrapInFunction(CallStmt(Call("dot", "v", "v")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/msl/generator_impl_call_test.cc b/src/tint/writer/msl/generator_impl_call_test.cc
index 1011ebd..75504fb 100644
--- a/src/tint/writer/msl/generator_impl_call_test.cc
+++ b/src/tint/writer/msl/generator_impl_call_test.cc
@@ -45,8 +45,8 @@
          utils::Vector{
              Return(1.23_f),
          });
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -65,8 +65,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), utils::Empty, utils::Empty);
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     auto* stmt = CallStmt(call);
diff --git a/src/tint/writer/msl/generator_impl_function_test.cc b/src/tint/writer/msl/generator_impl_function_test.cc
index addd255..3fa5a21 100644
--- a/src/tint/writer/msl/generator_impl_function_test.cc
+++ b/src/tint/writer/msl/generator_impl_function_test.cc
@@ -342,7 +342,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Group(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
               Binding(0_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
@@ -381,7 +381,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Group(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Group(0_a),
               Binding(0_a));
 
     auto* var = Var("v", ty.f32(), MemberAccessor("coord", "b"));
@@ -417,7 +417,7 @@
 TEST_F(MslGeneratorImplTest, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
     auto* ubo_ty = Structure("UBO", utils::Vector{Member("coord", ty.vec4<f32>())});
     auto* ubo =
-        GlobalVar("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform, Group(0_a), Binding(0_a));
+        GlobalVar("ubo", ty.Of(ubo_ty), ast::AddressSpace::kUniform, Group(0_a), Binding(0_a));
 
     Func("sub_func",
          utils::Vector{
@@ -467,7 +467,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Group(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
               Binding(0_a));
 
     Func("sub_func",
@@ -519,7 +519,7 @@
                                     Member("b", ty.f32()),
                                 });
 
-    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Group(0_a),
+    GlobalVar("coord", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Group(0_a),
               Binding(0_a));
 
     Func("sub_func",
@@ -657,7 +657,7 @@
 
     auto* s = Structure("Data", utils::Vector{Member("d", ty.f32())});
 
-    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Group(0_a),
+    GlobalVar("data", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
               Binding(0_a));
 
     {
diff --git a/src/tint/writer/msl/generator_impl_import_test.cc b/src/tint/writer/msl/generator_impl_import_test.cc
index 7b79569..4a81130 100644
--- a/src/tint/writer/msl/generator_impl_import_test.cc
+++ b/src/tint/writer/msl/generator_impl_import_test.cc
@@ -234,7 +234,7 @@
                                          MslImportData{"clamp", "clamp"}));
 
 TEST_F(MslGeneratorImplTest, MslImportData_Determinant) {
-    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = Call("determinant", "var");
 
diff --git a/src/tint/writer/msl/generator_impl_loop_test.cc b/src/tint/writer/msl/generator_impl_loop_test.cc
index 7c84db0..41017ff 100644
--- a/src/tint/writer/msl/generator_impl_loop_test.cc
+++ b/src/tint/writer/msl/generator_impl_loop_test.cc
@@ -64,8 +64,8 @@
 TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
     Func("a_statement", {}, ty.void_(), utils::Empty);
 
-    GlobalVar("lhs", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* body = Block(create<ast::DiscardStatement>());
     auto* continuing = Block(CallStmt(Call("a_statement")));
@@ -107,7 +107,7 @@
     // }
     //
 
-    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.4_f))),  //
                        Decl(Var("other", ty.f32())),             //
@@ -184,7 +184,7 @@
     Func("f", utils::Vector{Param("i", ty.i32())}, ty.void_(), utils::Empty);
     auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
 
-    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::AddressSpace::kWorkgroup);
     auto* multi_stmt = Block(f(1_i), f(2_i));
     auto* loop = For(multi_stmt, nullptr, nullptr,  //
                      Block(Return()));
@@ -260,7 +260,7 @@
     Func("f", utils::Vector{Param("i", ty.i32())}, ty.void_(), utils::Empty);
     auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
 
-    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::AddressSpace::kWorkgroup);
     auto* multi_stmt = Block(f(1_i), f(2_i));
     auto* loop = For(nullptr, nullptr, multi_stmt,  //
                      Block(Return()));
@@ -315,7 +315,7 @@
     Func("f", utils::Vector{Param("i", ty.i32())}, ty.void_(), utils::Empty);
     auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
 
-    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::AddressSpace::kWorkgroup);
     auto* multi_stmt_a = Block(f(1_i), f(2_i));
     auto* multi_stmt_b = Block(f(3_i), f(4_i));
     auto* loop = For(multi_stmt_a, Expr(true), multi_stmt_b,  //
diff --git a/src/tint/writer/msl/generator_impl_member_accessor_test.cc b/src/tint/writer/msl/generator_impl_member_accessor_test.cc
index e189910..2f16f4a 100644
--- a/src/tint/writer/msl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/msl/generator_impl_member_accessor_test.cc
@@ -21,7 +21,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor) {
     GlobalVar("str", ty.Of(Structure("my_str", utils::Vector{Member("mem", ty.f32())})),
-              ast::StorageClass::kPrivate);
+              ast::AddressSpace::kPrivate);
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(expr);
 
@@ -33,7 +33,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor_Swizzle_xyz) {
-    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = MemberAccessor("my_vec", "xyz");
     WrapInFunction(expr);
@@ -45,7 +45,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor_Swizzle_gbr) {
-    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::AddressSpace::kPrivate);
 
     auto* expr = MemberAccessor("my_vec", "gbr");
     WrapInFunction(expr);
diff --git a/src/tint/writer/msl/generator_impl_sanitizer_test.cc b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
index f96a1c9..71fe1f6 100644
--- a/src/tint/writer/msl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
@@ -28,7 +28,7 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -82,7 +82,7 @@
                                          Member(0, "z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -135,7 +135,7 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     auto* p = Let("p", AddressOf("b"));
@@ -192,9 +192,9 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(0_a));
-    GlobalVar("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(2_a),
+    GlobalVar("c", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(2_a),
               Group(0_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -251,9 +251,9 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniformMissingBinding) {
     auto* s = Structure("my_struct", utils::Vector{Member(0, "a", ty.array<f32>(4))});
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(0_a));
-    GlobalVar("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(2_a),
+    GlobalVar("c", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(2_a),
               Group(0_a));
 
     Func("a_func", utils::Empty, ty.void_(),
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index 9b3d0bc..bff94b0 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -154,7 +154,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrix) {
-    GlobalVar("m", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m", ty.mat2x2<f32>(), ast::AddressSpace::kWorkgroup);
     Func("comp_main", utils::Empty, ty.void_(), utils::Vector{Decl(Let("x", Expr("m")))},
          utils::Vector{
              Stage(ast::PipelineStage::kCompute),
@@ -194,7 +194,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrixInArray) {
-    GlobalVar("m", ty.array(ty.mat2x2<f32>(), 4_i), ast::StorageClass::kWorkgroup);
+    GlobalVar("m", ty.array(ty.mat2x2<f32>(), 4_i), ast::AddressSpace::kWorkgroup);
     Func("comp_main", utils::Empty, ty.void_(), utils::Vector{Decl(Let("x", Expr("m")))},
          utils::Vector{
              Stage(ast::PipelineStage::kCompute),
@@ -255,7 +255,7 @@
     Structure("S2", utils::Vector{
                         Member("s", ty.type_name("S1")),
                     });
-    GlobalVar("s", ty.type_name("S2"), ast::StorageClass::kWorkgroup);
+    GlobalVar("s", ty.type_name("S2"), ast::AddressSpace::kWorkgroup);
     Func("comp_main", utils::Empty, ty.void_(), utils::Vector{Decl(Let("x", Expr("s")))},
          utils::Vector{
              Stage(ast::PipelineStage::kCompute),
@@ -305,15 +305,15 @@
 }
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrix_Multiples) {
-    GlobalVar("m1", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m2", ty.mat2x3<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m3", ty.mat2x4<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m4", ty.mat3x2<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m5", ty.mat3x3<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m6", ty.mat3x4<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m7", ty.mat4x2<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m8", ty.mat4x3<f32>(), ast::StorageClass::kWorkgroup);
-    GlobalVar("m9", ty.mat4x4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m1", ty.mat2x2<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m2", ty.mat2x3<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m3", ty.mat2x4<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m4", ty.mat3x2<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m5", ty.mat3x3<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m6", ty.mat3x4<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m7", ty.mat4x2<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m8", ty.mat4x3<f32>(), ast::AddressSpace::kWorkgroup);
+    GlobalVar("m9", ty.mat4x4<f32>(), ast::AddressSpace::kWorkgroup);
     Func("main1", utils::Empty, ty.void_(),
          utils::Vector{
              Decl(Let("a1", Expr("m1"))),
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index c8f4f6d..bc9767a 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -89,7 +89,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_Array) {
     auto* arr = ty.array<bool, 4>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -101,7 +101,7 @@
 TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) {
     auto* a = ty.array<bool, 4>();
     auto* b = ty.array(a, 5_u);
-    GlobalVar("G", b, ast::StorageClass::kPrivate);
+    GlobalVar("G", b, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -114,7 +114,7 @@
     auto* a = ty.array<bool, 4>();
     auto* b = ty.array(a, 5_u);
     auto* c = ty.array(b, 6_u);
-    GlobalVar("G", c, ast::StorageClass::kPrivate);
+    GlobalVar("G", c, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -125,7 +125,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) {
     auto* arr = ty.array<bool, 4>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -136,7 +136,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) {
     auto* arr = ty.array<bool, 1>();
-    GlobalVar("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -211,7 +211,7 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_Pointer) {
     auto* f32 = create<sem::F32>();
-    auto* p = create<sem::Pointer>(f32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
+    auto* p = create<sem::Pointer>(f32, ast::AddressSpace::kWorkgroup, ast::Access::kReadWrite);
 
     GeneratorImpl& gen = Build();
 
@@ -255,7 +255,7 @@
     auto* s = Structure(
         "S", utils::Vector{
                  Member("a", ty.i32(), utils::Vector{MemberSize(32_a)}),
-                 Member("b", ty.f32(), utils::Vector{MemberAlign(128_u), MemberSize(128_a)}),
+                 Member("b", ty.f32(), utils::Vector{MemberAlign(128_i), MemberSize(128_a)}),
                  Member("c", ty.vec2<f32>()),
                  Member("d", ty.u32()),
                  Member("e", ty.vec3<f32>()),
@@ -282,7 +282,7 @@
                  Member("z", ty.f32()),
              });
 
-    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -372,7 +372,7 @@
     auto* inner_x =
         Structure("inner_x", utils::Vector{
                                  Member("a", ty.i32()),
-                                 Member("b", ty.f32(), utils::Vector{MemberAlign(512_u)}),
+                                 Member("b", ty.f32(), utils::Vector{MemberAlign(512_i)}),
                              });
 
     // inner_y: size(516), align(4)
@@ -390,7 +390,7 @@
                                  Member("e", ty.f32()),
                              });
 
-    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -460,7 +460,7 @@
     // inner: size(1024), align(512)
     auto* inner = Structure("inner", utils::Vector{
                                          Member("a", ty.i32()),
-                                         Member("b", ty.f32(), utils::Vector{MemberAlign(512_u)}),
+                                         Member("b", ty.f32(), utils::Vector{MemberAlign(512_i)}),
                                      });
 
     // array_x: size(28), align(4)
@@ -481,7 +481,7 @@
                                  Member("f", array_z),
                              });
 
-    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -564,7 +564,7 @@
                                  Member("c", ty.i32()),
                              });
 
-    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -598,7 +598,7 @@
                                  // uses symbols tint_pad_[0..9] and tint_pad_[20..35]
                                  Member("tint_pad_2", ty.i32(), utils::Vector{MemberSize(32_a)}),
                                  Member("tint_pad_20", ty.f32(),
-                                        utils::Vector{MemberAlign(128_u), MemberSize(128_u)}),
+                                        utils::Vector{MemberAlign(128_i), MemberSize(128_u)}),
                                  Member("tint_pad_33", ty.vec2<f32>()),
                                  Member("tint_pad_1", ty.u32()),
                                  Member("tint_pad_3", ty.vec3<f32>()),
@@ -625,7 +625,7 @@
                                  Member("tint_pad_21", ty.f32()),
                              });
 
-    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -683,7 +683,7 @@
                                  Member("b", ty.f32()),
                              });
 
-    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("G", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/msl/generator_impl_unary_op_test.cc b/src/tint/writer/msl/generator_impl_unary_op_test.cc
index 5f86bb5..0c234df 100644
--- a/src/tint/writer/msl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/msl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using MslUnaryOpTest = TestHelper;
 
 TEST_F(MslUnaryOpTest, AddressOf) {
-    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Complement) {
-    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Indirection) {
-    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::AddressSpace::kPrivate);
     auto* p = Let("expr", create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
     WrapInFunction(p, op);
@@ -57,7 +57,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Not) {
-    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -69,7 +69,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Negation) {
-    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index 3152f6c..b57a39f 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -518,7 +518,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     WrapInFunction(Expr("a"));
 
@@ -531,7 +531,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Workgroup) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kWorkgroup);
 
     WrapInFunction(Expr("a"));
 
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 70d16a6..874e0b5 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -477,8 +477,8 @@
     for (const auto* var : func_sem->TransitivelyReferencedGlobals()) {
         // For SPIR-V 1.3 we only output Input/output variables. If we update to
         // SPIR-V 1.4 or later this should be all variables.
-        if (var->StorageClass() != ast::StorageClass::kIn &&
-            var->StorageClass() != ast::StorageClass::kOut) {
+        if (var->AddressSpace() != ast::AddressSpace::kIn &&
+            var->AddressSpace() != ast::AddressSpace::kOut) {
             continue;
         }
 
@@ -679,7 +679,7 @@
 
     auto result = result_op();
     auto var_id = std::get<uint32_t>(result);
-    auto sc = ast::StorageClass::kFunction;
+    auto sc = ast::AddressSpace::kFunction;
     auto* type = sem->Type();
     auto type_id = GenerateTypeIfNeeded(type);
     if (type_id == 0) {
@@ -695,7 +695,7 @@
         return 0;
     }
     push_function_var(
-        {Operand(type_id), result, U32Operand(ConvertStorageClass(sc)), Operand(null_id)});
+        {Operand(type_id), result, U32Operand(ConvertAddressSpace(sc)), Operand(null_id)});
 
     if (v->constructor) {
         if (!GenerateStore(var_id, init_id)) {
@@ -738,8 +738,8 @@
     auto result = result_op();
     auto var_id = std::get<uint32_t>(result);
 
-    auto sc = sem->StorageClass() == ast::StorageClass::kNone ? ast::StorageClass::kPrivate
-                                                              : sem->StorageClass();
+    auto sc = sem->AddressSpace() == ast::AddressSpace::kNone ? ast::AddressSpace::kPrivate
+                                                              : sem->AddressSpace();
 
     auto type_id = GenerateTypeIfNeeded(sem->Type());
     if (type_id == 0) {
@@ -748,7 +748,7 @@
 
     push_debug(spv::Op::OpName, {Operand(var_id), Operand(builder_.Symbols().NameFor(v->symbol))});
 
-    OperandList ops = {Operand(type_id), result, U32Operand(ConvertStorageClass(sc))};
+    OperandList ops = {Operand(type_id), result, U32Operand(ConvertAddressSpace(sc))};
 
     if (v->constructor) {
         ops.push_back(Operand(init_id));
@@ -777,10 +777,10 @@
             // If we're a Workgroup variable, and the
             // VK_KHR_zero_initialize_workgroup_memory extension is enabled, we should
             // also zero-initialize.
-            if (sem->StorageClass() == ast::StorageClass::kPrivate ||
-                sem->StorageClass() == ast::StorageClass::kOut ||
+            if (sem->AddressSpace() == ast::AddressSpace::kPrivate ||
+                sem->AddressSpace() == ast::AddressSpace::kOut ||
                 (zero_initialize_workgroup_memory_ &&
-                 sem->StorageClass() == ast::StorageClass::kWorkgroup)) {
+                 sem->AddressSpace() == ast::AddressSpace::kWorkgroup)) {
                 init_id = GenerateConstantNullIfNeeded(type);
                 if (init_id == 0) {
                     return 0;
@@ -798,7 +798,7 @@
             [&](const ast::BuiltinAttribute* builtin) {
                 push_annot(spv::Op::OpDecorate,
                            {Operand(var_id), U32Operand(SpvDecorationBuiltIn),
-                            U32Operand(ConvertBuiltin(builtin->builtin, sem->StorageClass()))});
+                            U32Operand(ConvertBuiltin(builtin->builtin, sem->AddressSpace()))});
                 return true;
             },
             [&](const ast::LocationAttribute*) {
@@ -1875,10 +1875,10 @@
 uint32_t Builder::GenerateSplat(uint32_t scalar_id, const sem::Type* vec_type) {
     // Create a new vector to splat scalar into
     auto splat_vector = result_op();
-    auto* splat_vector_type = builder_.create<sem::Pointer>(vec_type, ast::StorageClass::kFunction,
+    auto* splat_vector_type = builder_.create<sem::Pointer>(vec_type, ast::AddressSpace::kFunction,
                                                             ast::Access::kReadWrite);
     push_function_var({Operand(GenerateTypeIfNeeded(splat_vector_type)), splat_vector,
-                       U32Operand(ConvertStorageClass(ast::StorageClass::kFunction)),
+                       U32Operand(ConvertAddressSpace(ast::AddressSpace::kFunction)),
                        Operand(GenerateConstantNullIfNeeded(vec_type))});
 
     // Splat scalar into vector
@@ -3041,21 +3041,21 @@
                                     Operand result_id) {
     auto is_value_signed = [&] { return builtin->Parameters()[1]->Type()->Is<sem::I32>(); };
 
-    auto storage_class = builtin->Parameters()[0]->Type()->As<sem::Pointer>()->StorageClass();
+    auto address_space = builtin->Parameters()[0]->Type()->As<sem::Pointer>()->AddressSpace();
 
     uint32_t memory_id = 0;
-    switch (builtin->Parameters()[0]->Type()->As<sem::Pointer>()->StorageClass()) {
-        case ast::StorageClass::kWorkgroup:
+    switch (builtin->Parameters()[0]->Type()->As<sem::Pointer>()->AddressSpace()) {
+        case ast::AddressSpace::kWorkgroup:
             memory_id = GenerateConstantIfNeeded(
                 ScalarConstant::U32(static_cast<uint32_t>(spv::Scope::Workgroup)));
             break;
-        case ast::StorageClass::kStorage:
+        case ast::AddressSpace::kStorage:
             memory_id = GenerateConstantIfNeeded(
                 ScalarConstant::U32(static_cast<uint32_t>(spv::Scope::Device)));
             break;
         default:
             TINT_UNREACHABLE(Writer, builder_.Diagnostics())
-                << "unhandled atomic storage class " << storage_class;
+                << "unhandled atomic address space " << address_space;
             return false;
     }
     if (memory_id == 0) {
@@ -3685,10 +3685,10 @@
     // fine.
     if (auto* ptr = type->As<sem::Pointer>()) {
         type =
-            builder_.create<sem::Pointer>(ptr->StoreType(), ptr->StorageClass(), ast::kReadWrite);
+            builder_.create<sem::Pointer>(ptr->StoreType(), ptr->AddressSpace(), ast::kReadWrite);
     } else if (auto* ref = type->As<sem::Reference>()) {
         type =
-            builder_.create<sem::Pointer>(ref->StoreType(), ref->StorageClass(), ast::kReadWrite);
+            builder_.create<sem::Pointer>(ref->StoreType(), ref->AddressSpace(), ast::kReadWrite);
     }
 
     return utils::GetOrCreate(type_to_id_, type, [&]() -> uint32_t {
@@ -3905,9 +3905,9 @@
         return false;
     }
 
-    auto stg_class = ConvertStorageClass(ptr->StorageClass());
+    auto stg_class = ConvertAddressSpace(ptr->AddressSpace());
     if (stg_class == SpvStorageClassMax) {
-        error_ = "invalid storage class for pointer";
+        error_ = "invalid address space for pointer";
         return false;
     }
 
@@ -3922,9 +3922,9 @@
         return false;
     }
 
-    auto stg_class = ConvertStorageClass(ref->StorageClass());
+    auto stg_class = ConvertAddressSpace(ref->AddressSpace());
     if (stg_class == SpvStorageClassMax) {
-        error_ = "invalid storage class for reference";
+        error_ = "invalid address space for reference";
         return false;
     }
 
@@ -4007,43 +4007,43 @@
     return true;
 }
 
-SpvStorageClass Builder::ConvertStorageClass(ast::StorageClass klass) const {
+SpvStorageClass Builder::ConvertAddressSpace(ast::AddressSpace klass) const {
     switch (klass) {
-        case ast::StorageClass::kInvalid:
+        case ast::AddressSpace::kInvalid:
             return SpvStorageClassMax;
-        case ast::StorageClass::kIn:
+        case ast::AddressSpace::kIn:
             return SpvStorageClassInput;
-        case ast::StorageClass::kOut:
+        case ast::AddressSpace::kOut:
             return SpvStorageClassOutput;
-        case ast::StorageClass::kUniform:
+        case ast::AddressSpace::kUniform:
             return SpvStorageClassUniform;
-        case ast::StorageClass::kWorkgroup:
+        case ast::AddressSpace::kWorkgroup:
             return SpvStorageClassWorkgroup;
-        case ast::StorageClass::kPushConstant:
+        case ast::AddressSpace::kPushConstant:
             return SpvStorageClassPushConstant;
-        case ast::StorageClass::kHandle:
+        case ast::AddressSpace::kHandle:
             return SpvStorageClassUniformConstant;
-        case ast::StorageClass::kStorage:
+        case ast::AddressSpace::kStorage:
             return SpvStorageClassStorageBuffer;
-        case ast::StorageClass::kPrivate:
+        case ast::AddressSpace::kPrivate:
             return SpvStorageClassPrivate;
-        case ast::StorageClass::kFunction:
+        case ast::AddressSpace::kFunction:
             return SpvStorageClassFunction;
-        case ast::StorageClass::kNone:
+        case ast::AddressSpace::kNone:
             break;
     }
     return SpvStorageClassMax;
 }
 
-SpvBuiltIn Builder::ConvertBuiltin(ast::BuiltinValue builtin, ast::StorageClass storage) {
+SpvBuiltIn Builder::ConvertBuiltin(ast::BuiltinValue builtin, ast::AddressSpace storage) {
     switch (builtin) {
         case ast::BuiltinValue::kPosition:
-            if (storage == ast::StorageClass::kIn) {
+            if (storage == ast::AddressSpace::kIn) {
                 return SpvBuiltInFragCoord;
-            } else if (storage == ast::StorageClass::kOut) {
+            } else if (storage == ast::AddressSpace::kOut) {
                 return SpvBuiltInPosition;
             } else {
-                TINT_ICE(Writer, builder_.Diagnostics()) << "invalid storage class for builtin";
+                TINT_ICE(Writer, builder_.Diagnostics()) << "invalid address space for builtin";
                 break;
             }
         case ast::BuiltinValue::kVertexIndex:
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index ba88443..e081d31 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -76,7 +76,7 @@
     /// Constructor
     /// @param program the program
     /// @param zero_initialize_workgroup_memory `true` to initialize all the
-    /// variables in the Workgroup storage class with OpConstantNull
+    /// variables in the Workgroup address space with OpConstantNull
     explicit Builder(const Program* program, bool zero_initialize_workgroup_memory = false);
     ~Builder();
 
@@ -203,15 +203,15 @@
     /// inside a basic block.
     bool InsideBasicBlock() const;
 
-    /// Converts a storage class to a SPIR-V storage class.
-    /// @param klass the storage class to convert
-    /// @returns the SPIR-V storage class or SpvStorageClassMax on error.
-    SpvStorageClass ConvertStorageClass(ast::StorageClass klass) const;
+    /// Converts a address space to a SPIR-V address space.
+    /// @param klass the address space to convert
+    /// @returns the SPIR-V address space or SpvStorageClassMax on error.
+    SpvStorageClass ConvertAddressSpace(ast::AddressSpace klass) const;
     /// Converts a builtin to a SPIR-V builtin and pushes a capability if needed.
     /// @param builtin the builtin to convert
-    /// @param storage the storage class that this builtin is being used with
+    /// @param storage the address space that this builtin is being used with
     /// @returns the SPIR-V builtin or SpvBuiltInMax on error.
-    SpvBuiltIn ConvertBuiltin(ast::BuiltinValue builtin, ast::StorageClass storage);
+    SpvBuiltIn ConvertBuiltin(ast::BuiltinValue builtin, ast::AddressSpace storage);
 
     /// Converts an interpolate attribute to SPIR-V decorations and pushes a
     /// capability if needed.
diff --git a/src/tint/writer/spirv/builder_assign_test.cc b/src/tint/writer/spirv/builder_assign_test.cc
index 963e680..d43fbbe 100644
--- a/src/tint/writer/spirv/builder_assign_test.cc
+++ b/src/tint/writer/spirv/builder_assign_test.cc
@@ -23,7 +23,7 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, Assign_Var) {
-    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* assign = Assign("var", 1_f);
 
@@ -51,7 +51,7 @@
 }
 
 TEST_F(BuilderTest, Assign_Var_OutsideFunction_IsError) {
-    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* assign = Assign("var", Expr(1_f));
 
@@ -70,7 +70,7 @@
 }
 
 TEST_F(BuilderTest, Assign_Var_ZeroConstructor) {
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* val = vec3<f32>();
     auto* assign = Assign("var", val);
@@ -101,7 +101,7 @@
 TEST_F(BuilderTest, Assign_Var_Complex_ConstructorNestedVector) {
     auto* init = vec3<f32>(vec2<f32>(1_f, 2_f), 3_f);
 
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* assign = Assign("var", init);
 
@@ -134,7 +134,7 @@
 TEST_F(BuilderTest, Assign_Var_Complex_Constructor) {
     auto* init = vec3<f32>(1_f, 2_f, 3_f);
 
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* assign = Assign("var", init);
 
@@ -209,7 +209,7 @@
 }
 
 TEST_F(BuilderTest, Assign_Vector) {
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* val = vec3<f32>(1_f, 1_f, 3_f);
     auto* assign = Assign("var", val);
@@ -243,7 +243,7 @@
 TEST_F(BuilderTest, Assign_Vector_MemberByName) {
     // var.y = 1
 
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* assign = Assign(MemberAccessor("var", "y"), Expr(1_f));
 
@@ -278,7 +278,7 @@
 TEST_F(BuilderTest, Assign_Vector_MemberByIndex) {
     // var[1] = 1
 
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* assign = Assign(IndexAccessor("var", 1_i), Expr(1_f));
 
diff --git a/src/tint/writer/spirv/builder_binary_expression_test.cc b/src/tint/writer/spirv/builder_binary_expression_test.cc
index fd9a64a..e7237fa 100644
--- a/src/tint/writer/spirv/builder_binary_expression_test.cc
+++ b/src/tint/writer/spirv/builder_binary_expression_test.cc
@@ -1031,8 +1031,8 @@
 }
 
 TEST_F(BuilderTest, Binary_LogicalAnd_WithLoads) {
-    auto* a_var = GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate, Expr(true));
-    auto* b_var = GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate, Expr(false));
+    auto* a_var = GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate, Expr(true));
+    auto* b_var = GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate, Expr(false));
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
 
     WrapInFunction(expr);
@@ -1197,8 +1197,8 @@
 }
 
 TEST_F(BuilderTest, Binary_LogicalOr_WithLoads) {
-    auto* a_var = GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate, Expr(true));
-    auto* b_var = GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate, Expr(false));
+    auto* a_var = GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate, Expr(true));
+    auto* b_var = GlobalVar("b", ty.bool_(), ast::AddressSpace::kPrivate, Expr(false));
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
 
@@ -1241,19 +1241,19 @@
     switch (type) {
         case Type::f32:
             builder->GlobalVar(name, builder->ty.vec3<f32>(), builder->vec3<f32>(1_f, 1_f, 1_f),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::f16:
             builder->GlobalVar(name, builder->ty.vec3<f16>(), builder->vec3<f16>(1_h, 1_h, 1_h),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::i32:
             builder->GlobalVar(name, builder->ty.vec3<i32>(), builder->vec3<i32>(1_i, 1_i, 1_i),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::u32:
             builder->GlobalVar(name, builder->ty.vec3<u32>(), builder->vec3<u32>(1_u, 1_u, 1_u),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
     }
     return builder->Expr(name);
@@ -1263,19 +1263,19 @@
     switch (type) {
         case Type::f32:
             builder->GlobalVar(name, builder->ty.f32(), builder->Expr(1_f),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::f16:
             builder->GlobalVar(name, builder->ty.f16(), builder->Expr(1_h),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::i32:
             builder->GlobalVar(name, builder->ty.i32(), builder->Expr(1_i),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::u32:
             builder->GlobalVar(name, builder->ty.u32(), builder->Expr(1_u),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
     }
     return builder->Expr(name);
@@ -1570,11 +1570,11 @@
     switch (type) {
         case Type::f32:
             builder->GlobalVar(name, builder->ty.mat3x4<f32>(), builder->mat3x4<f32>(),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::f16:
             builder->GlobalVar(name, builder->ty.mat3x4<f16>(), builder->mat3x4<f16>(),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
     }
     return builder->Expr(name);
@@ -1584,11 +1584,11 @@
     switch (type) {
         case Type::f32:
             builder->GlobalVar(name, builder->ty.mat4x3<f32>(), builder->mat4x3<f32>(),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
             break;
         case Type::f16:
             builder->GlobalVar(name, builder->ty.mat4x3<f16>(), builder->mat4x3<f16>(),
-                               ast::StorageClass::kPrivate);
+                               ast::AddressSpace::kPrivate);
     }
     return builder->Expr(name);
 }
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index cfb85d9..dddc475 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -97,7 +97,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_GLSLMethod_WithLoad_f32) {
-    auto* var = GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("ident", ty.f32(), ast::AddressSpace::kPrivate);
     auto* expr = Call("round", "ident");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -133,7 +133,7 @@
 TEST_F(BuiltinBuilderTest, Call_GLSLMethod_WithLoad_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("ident", ty.f16(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("ident", ty.f16(), ast::AddressSpace::kPrivate);
     auto* expr = Call("round", "ident");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -172,7 +172,7 @@
 using BuiltinBoolTest = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(BuiltinBoolTest, Call_Bool_Scalar) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.bool_(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -198,7 +198,7 @@
 
 TEST_P(BuiltinBoolTest, Call_Bool_Vector) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<bool>(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -231,9 +231,9 @@
                          testing::Values(BuiltinData{"any", "OpAny"}, BuiltinData{"all", "OpAll"}));
 
 TEST_F(BuiltinBuilderTest, Call_Select) {
-    auto* v3 = GlobalVar("v3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v3 = GlobalVar("v3", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
-    auto* bool_v3 = GlobalVar("bool_v3", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    auto* bool_v3 = GlobalVar("bool_v3", ty.vec3<bool>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("select", "v3", "v3", "bool_v3");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -277,7 +277,7 @@
     auto* s = Structure("my_struct", utils::Vector{
                                          Member("a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
     auto* expr = Call("arrayLength", AddressOf(MemberAccessor("b", "a")));
 
@@ -321,7 +321,7 @@
                                          Member("z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
     auto* expr = Call("arrayLength", AddressOf(MemberAccessor("b", "a")));
 
@@ -364,7 +364,7 @@
     auto* s = Structure("my_struct", utils::Vector{
                                          Member("a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     auto* p = Let("p", AddressOf("b"));
@@ -423,7 +423,7 @@
     auto* s = Structure("my_struct", utils::Vector{
                                          Member("a", ty.array<f32>(4)),
                                      });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(1_a),
               Group(2_a));
 
     auto* p = Let("p", AddressOf(Deref(AddressOf("b"))));
@@ -1862,7 +1862,7 @@
 using BuiltinIntTest = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(BuiltinIntTest, Call_SInt_Scalar) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -1892,7 +1892,7 @@
 
 TEST_P(BuiltinIntTest, Call_SInt_Vector) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -1923,7 +1923,7 @@
 
 TEST_P(BuiltinIntTest, Call_UInt_Scalar) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.u32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.u32(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -1953,7 +1953,7 @@
 
 TEST_P(BuiltinIntTest, Call_UInt_Vector) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<u32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -2824,7 +2824,7 @@
 namespace matrix_builtin_tests {
 
 TEST_F(BuiltinBuilderTest, Call_Determinant_f32) {
-    auto* var = GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.mat3x3<f32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("determinant", "var");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -2861,7 +2861,7 @@
 TEST_F(BuiltinBuilderTest, Call_Determinant_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("var", ty.mat3x3<f16>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.mat3x3<f16>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("determinant", "var");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -2896,7 +2896,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Transpose_f32) {
-    auto* var = GlobalVar("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.mat2x3<f32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("transpose", "var");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -2934,7 +2934,7 @@
 TEST_F(BuiltinBuilderTest, Call_Transpose_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("var", ty.mat2x3<f16>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.mat2x3<f16>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("transpose", "var");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -2975,7 +2975,7 @@
 namespace vector_builtin_tests {
 
 TEST_F(BuiltinBuilderTest, Call_Dot_F32) {
-    auto* var = GlobalVar("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -3006,7 +3006,7 @@
 TEST_F(BuiltinBuilderTest, Call_Dot_F16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("v", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -3035,7 +3035,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Dot_U32) {
-    auto* var = GlobalVar("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<u32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -3074,7 +3074,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Dot_I32) {
-    auto* var = GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -3120,7 +3120,7 @@
 using BuiltinDeriveTest = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(BuiltinDeriveTest, Call_Derivative_Scalar) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.f32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.f32(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -3153,7 +3153,7 @@
 
 TEST_P(BuiltinDeriveTest, Call_Derivative_Vector) {
     auto param = GetParam();
-    auto* var = GlobalVar("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -3223,7 +3223,7 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -3286,7 +3286,7 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -3357,7 +3357,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("v", ty.atomic<i32>()),
                              });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -3429,7 +3429,7 @@
     auto* s = Structure("S", utils::Vector{
                                  Member("v", ty.atomic<u32>()),
                              });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -3503,7 +3503,7 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
@@ -3578,7 +3578,7 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(1_a),
+    GlobalVar("b", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(1_a),
               Group(2_a));
 
     Func("a_func", utils::Empty, ty.void_(),
diff --git a/src/tint/writer/spirv/builder_constructor_expression_test.cc b/src/tint/writer/spirv/builder_constructor_expression_test.cc
index cb8dd4f..cabbd18 100644
--- a/src/tint/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_constructor_expression_test.cc
@@ -24,7 +24,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Const) {
     auto* c = Expr(42.2_f);
-    auto* g = GlobalVar("g", ty.f32(), c, ast::StorageClass::kPrivate);
+    auto* g = GlobalVar("g", ty.f32(), c, ast::AddressSpace::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -1935,7 +1935,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_F32_With_F32) {
     auto* ctor = Construct<f32>(2_f);
-    GlobalVar("g", ty.f32(), ast::StorageClass::kPrivate, ctor);
+    GlobalVar("g", ty.f32(), ast::AddressSpace::kPrivate, ctor);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -1954,7 +1954,7 @@
     Enable(ast::Extension::kF16);
 
     auto* ctor = Construct<f16>(2_h);
-    GlobalVar("g", ty.f16(), ast::StorageClass::kPrivate, ctor);
+    GlobalVar("g", ty.f16(), ast::AddressSpace::kPrivate, ctor);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2015,7 +2015,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_U32_With_F32) {
     auto* ctor = Construct<u32>(1.5_f);
-    GlobalVar("g", ty.u32(), ast::StorageClass::kPrivate, ctor);
+    GlobalVar("g", ty.u32(), ast::AddressSpace::kPrivate, ctor);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2034,7 +2034,7 @@
     Enable(ast::Extension::kF16);
 
     auto* ctor = Construct<u32>(1.5_h);
-    GlobalVar("g", ty.u32(), ast::StorageClass::kPrivate, ctor);
+    GlobalVar("g", ty.u32(), ast::AddressSpace::kPrivate, ctor);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2099,7 +2099,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_F32) {
     auto* cast = vec2<f32>(2_f);
-    auto* g = GlobalVar("g", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec2<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2117,7 +2117,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec2<f16>(2_h);
-    auto* g = GlobalVar("g", ty.vec2<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec2<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2181,7 +2181,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_F32_With_Vec2) {
     auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
-    GlobalVar("a", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
+    GlobalVar("a", ty.vec2<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2203,7 +2203,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec2<f16>(vec2<f16>(2_h, 2_h));
-    GlobalVar("a", ty.vec2<f16>(), ast::StorageClass::kPrivate, cast);
+    GlobalVar("a", ty.vec2<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2271,7 +2271,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_F32_With_Vec3) {
     auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
-    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+    GlobalVar("a", ty.vec3<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2293,7 +2293,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec3<f16>(vec3<f16>(2_h, 2_h, 2_h));
-    GlobalVar("a", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+    GlobalVar("a", ty.vec3<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2361,7 +2361,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_F32_With_Vec4) {
     auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
-    GlobalVar("a", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    GlobalVar("a", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2383,7 +2383,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(vec4<f16>(2_h, 2_h, 2_h, 2_h));
-    GlobalVar("a", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    GlobalVar("a", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -2451,7 +2451,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32) {
     auto* cast = vec3<f32>(2_f);
-    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2469,7 +2469,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec3<f16>(2_h);
-    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2533,7 +2533,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32_Vec2) {
     auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2551,7 +2551,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec3<f16>(2_h, vec2<f16>(2_h, 2_h));
-    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2615,7 +2615,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec2_F32) {
     auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
-    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2633,7 +2633,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec3<f16>(vec2<f16>(2_h, 2_h), 2_h);
-    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec3<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2697,7 +2697,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32) {
     auto* cast = vec4<f32>(2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2715,7 +2715,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(2_h);
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2779,7 +2779,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_F32_Vec2) {
     auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2797,7 +2797,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(2_h, 2_h, vec2<f16>(2_h, 2_h));
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2861,7 +2861,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec2_F32) {
     auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2879,7 +2879,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(2_h, vec2<f16>(2_h, 2_h), 2_h);
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2943,7 +2943,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_F32_F32) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -2961,7 +2961,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(vec2<f16>(2_h, 2_h), 2_h, 2_h);
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -3025,7 +3025,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_F32_With_Vec2_Vec2) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -3043,7 +3043,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(vec2<f16>(2_h, 2_h), vec2<f16>(2_h, 2_h));
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -3082,7 +3082,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec3) {
     auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -3100,7 +3100,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(2_h, vec3<f16>(2_h, 2_h, 2_h));
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -3164,7 +3164,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec3_F32) {
     auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
-    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -3182,7 +3182,7 @@
     Enable(ast::Extension::kF16);
 
     auto* cast = vec4<f16>(vec3<f16>(2_h, 2_h, 2_h), 2_h);
-    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::StorageClass::kPrivate, cast);
+    auto* g = GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -4204,7 +4204,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_I32) {
-    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<i32>("i");
     WrapInFunction(cast);
@@ -4230,7 +4230,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F32_to_I32) {
-    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<i32>("i");
     WrapInFunction(cast);
@@ -4258,7 +4258,7 @@
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F16_to_I32) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<i32>("i");
     WrapInFunction(cast);
@@ -4284,7 +4284,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_U32) {
-    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<u32>("i");
     WrapInFunction(cast);
@@ -4310,7 +4310,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F32_to_U32) {
-    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<u32>("i");
     WrapInFunction(cast);
@@ -4338,7 +4338,7 @@
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F16_to_U32) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<u32>("i");
     WrapInFunction(cast);
@@ -4364,7 +4364,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_F32) {
-    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<f32>("i");
     WrapInFunction(cast);
@@ -4390,7 +4390,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_F32) {
-    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<f32>("i");
     WrapInFunction(cast);
@@ -4418,7 +4418,7 @@
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F16_to_F32) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f16>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<f32>("i");
     WrapInFunction(cast);
@@ -4446,7 +4446,7 @@
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_F16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<f16>("i");
     WrapInFunction(cast);
@@ -4474,7 +4474,7 @@
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_F16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<f16>("i");
     WrapInFunction(cast);
@@ -4502,7 +4502,7 @@
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F32_to_F16) {
     Enable(ast::Extension::kF16);
 
-    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* cast = vec3<f16>("i");
     WrapInFunction(cast);
@@ -4590,9 +4590,9 @@
 TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Vector_WithIdent) {
     // vec3<f32>(a, b, c)  -> false
 
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("c", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("c", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* t = vec3<f32>("a", "b", "c");
     WrapInFunction(t);
@@ -4662,8 +4662,8 @@
                                          Member("b", ty.vec3<f32>()),
                                      });
 
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::AddressSpace::kPrivate);
 
     auto* t = Construct(ty.Of(s), "a", "b");
     WrapInFunction(t);
diff --git a/src/tint/writer/spirv/builder_entry_point_test.cc b/src/tint/writer/spirv/builder_entry_point_test.cc
index a4128c1..4cdebf2 100644
--- a/src/tint/writer/spirv/builder_entry_point_test.cc
+++ b/src/tint/writer/spirv/builder_entry_point_test.cc
@@ -15,12 +15,12 @@
 #include <memory>
 
 #include "gtest/gtest.h"
+#include "src/tint/ast/address_space.h"
 #include "src/tint/ast/builtin_attribute.h"
 #include "src/tint/ast/builtin_value.h"
 #include "src/tint/ast/location_attribute.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/stage_attribute.h"
-#include "src/tint/ast/storage_class.h"
 #include "src/tint/ast/variable.h"
 #include "src/tint/program.h"
 #include "src/tint/sem/f32.h"
@@ -62,7 +62,7 @@
     ASSERT_TRUE(b.Build());
 
     // Test that "coord" and "loc1" get hoisted out to global variables with the
-    // Input storage class, retaining their decorations.
+    // Input address space, retaining their decorations.
     EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %19 "frag_main" %1 %5
@@ -142,7 +142,7 @@
     ASSERT_TRUE(b.Build());
 
     // Test that the return value gets hoisted out to a global variable with the
-    // Output storage class, and the return statements are replaced with stores.
+    // Output address space, and the return statements are replaced with stores.
     EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader
 OpMemoryModel Logical GLSL450
 OpEntryPoint Fragment %21 "frag_main" %1 %4
diff --git a/src/tint/writer/spirv/builder_function_test.cc b/src/tint/writer/spirv/builder_function_test.cc
index 449f937..b28817c 100644
--- a/src/tint/writer/spirv/builder_function_test.cc
+++ b/src/tint/writer/spirv/builder_function_test.cc
@@ -61,7 +61,7 @@
 }
 
 TEST_F(BuilderTest, Function_Terminator_ReturnValue) {
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     Func("a_func", utils::Empty, ty.f32(), utils::Vector{Return("a")}, utils::Empty);
 
@@ -198,7 +198,7 @@
 
     auto* s = Structure("Data", utils::Vector{Member("d", ty.f32())});
 
-    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("data", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(0_a));
 
     {
diff --git a/src/tint/writer/spirv/builder_function_variable_test.cc b/src/tint/writer/spirv/builder_function_variable_test.cc
index 40bc85d..2738634 100644
--- a/src/tint/writer/spirv/builder_function_variable_test.cc
+++ b/src/tint/writer/spirv/builder_function_variable_test.cc
@@ -22,8 +22,8 @@
 
 using BuilderTest = TestHelper;
 
-TEST_F(BuilderTest, FunctionVar_NoStorageClass) {
-    auto* v = Var("var", ty.f32(), ast::StorageClass::kFunction);
+TEST_F(BuilderTest, FunctionVar_NoAddressSpace) {
+    auto* v = Var("var", ty.f32(), ast::AddressSpace::kFunction);
     WrapInFunction(v);
 
     spirv::Builder& b = Build();
@@ -45,7 +45,7 @@
 
 TEST_F(BuilderTest, FunctionVar_WithConstantConstructor) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
-    auto* v = Var("var", ty.vec3<f32>(), ast::StorageClass::kFunction, init);
+    auto* v = Var("var", ty.vec3<f32>(), ast::AddressSpace::kFunction, init);
     WrapInFunction(v);
 
     spirv::Builder& b = Build();
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index 2c53023..b0bbf3f 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -24,8 +24,8 @@
 
 using BuilderTest = TestHelper;
 
-TEST_F(BuilderTest, GlobalVar_WithStorageClass) {
-    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
+TEST_F(BuilderTest, GlobalVar_WithAddressSpace) {
+    auto* v = GlobalVar("var", ty.f32(), ast::AddressSpace::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -42,7 +42,7 @@
 TEST_F(BuilderTest, GlobalVar_WithConstructor) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
-    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate, init);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::AddressSpace::kPrivate, init);
 
     spirv::Builder& b = Build();
 
@@ -66,7 +66,7 @@
     // var v = c;
 
     auto* c = GlobalConst("c", Expr(42_a));
-    GlobalVar("v", ast::StorageClass::kPrivate, Expr(c));
+    GlobalVar("v", ast::AddressSpace::kPrivate, Expr(c));
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -91,7 +91,7 @@
     // var v = c;
 
     auto* c = GlobalConst("c", vec3<f32>(1_f, 2_f, 3_f));
-    GlobalVar("v", ast::StorageClass::kPrivate, Expr(c));
+    GlobalVar("v", ast::AddressSpace::kPrivate, Expr(c));
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -121,7 +121,7 @@
     Enable(ast::Extension::kF16);
 
     auto* c = GlobalConst("c", vec3<f16>(1_h, 2_h, 3_h));
-    GlobalVar("v", ast::StorageClass::kPrivate, Expr(c));
+    GlobalVar("v", ast::AddressSpace::kPrivate, Expr(c));
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -150,7 +150,7 @@
     // var v = c;
 
     auto* c = GlobalConst("c", Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
-    GlobalVar("v", ast::StorageClass::kPrivate, Expr(c));
+    GlobalVar("v", ast::AddressSpace::kPrivate, Expr(c));
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -179,7 +179,7 @@
     // var v = c;
 
     auto* c = GlobalConst("c", Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
-    GlobalVar("v", ast::StorageClass::kPrivate, Expr(c));
+    GlobalVar("v", ast::AddressSpace::kPrivate, Expr(c));
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -208,7 +208,7 @@
     // var v = c;
 
     auto* c = GlobalConst("c", vec3<f32>(vec2<f32>(1_f, 2_f), 3_f));
-    GlobalVar("v", ast::StorageClass::kPrivate, Expr(c));
+    GlobalVar("v", ast::AddressSpace::kPrivate, Expr(c));
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -251,7 +251,7 @@
 
 struct BuiltinData {
     ast::BuiltinValue builtin;
-    ast::StorageClass storage;
+    ast::AddressSpace storage;
     SpvBuiltIn result;
 };
 inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
@@ -270,30 +270,30 @@
     BuilderTest_Type,
     BuiltinDataTest,
     testing::Values(
-        BuiltinData{ast::BuiltinValue::kInvalid, ast::StorageClass::kNone, SpvBuiltInMax},
-        BuiltinData{ast::BuiltinValue::kPosition, ast::StorageClass::kIn, SpvBuiltInFragCoord},
-        BuiltinData{ast::BuiltinValue::kPosition, ast::StorageClass::kOut, SpvBuiltInPosition},
+        BuiltinData{ast::BuiltinValue::kInvalid, ast::AddressSpace::kNone, SpvBuiltInMax},
+        BuiltinData{ast::BuiltinValue::kPosition, ast::AddressSpace::kIn, SpvBuiltInFragCoord},
+        BuiltinData{ast::BuiltinValue::kPosition, ast::AddressSpace::kOut, SpvBuiltInPosition},
         BuiltinData{
             ast::BuiltinValue::kVertexIndex,
-            ast::StorageClass::kIn,
+            ast::AddressSpace::kIn,
             SpvBuiltInVertexIndex,
         },
-        BuiltinData{ast::BuiltinValue::kInstanceIndex, ast::StorageClass::kIn,
+        BuiltinData{ast::BuiltinValue::kInstanceIndex, ast::AddressSpace::kIn,
                     SpvBuiltInInstanceIndex},
-        BuiltinData{ast::BuiltinValue::kFrontFacing, ast::StorageClass::kIn, SpvBuiltInFrontFacing},
-        BuiltinData{ast::BuiltinValue::kFragDepth, ast::StorageClass::kOut, SpvBuiltInFragDepth},
-        BuiltinData{ast::BuiltinValue::kLocalInvocationId, ast::StorageClass::kIn,
+        BuiltinData{ast::BuiltinValue::kFrontFacing, ast::AddressSpace::kIn, SpvBuiltInFrontFacing},
+        BuiltinData{ast::BuiltinValue::kFragDepth, ast::AddressSpace::kOut, SpvBuiltInFragDepth},
+        BuiltinData{ast::BuiltinValue::kLocalInvocationId, ast::AddressSpace::kIn,
                     SpvBuiltInLocalInvocationId},
-        BuiltinData{ast::BuiltinValue::kLocalInvocationIndex, ast::StorageClass::kIn,
+        BuiltinData{ast::BuiltinValue::kLocalInvocationIndex, ast::AddressSpace::kIn,
                     SpvBuiltInLocalInvocationIndex},
-        BuiltinData{ast::BuiltinValue::kGlobalInvocationId, ast::StorageClass::kIn,
+        BuiltinData{ast::BuiltinValue::kGlobalInvocationId, ast::AddressSpace::kIn,
                     SpvBuiltInGlobalInvocationId},
-        BuiltinData{ast::BuiltinValue::kWorkgroupId, ast::StorageClass::kIn, SpvBuiltInWorkgroupId},
-        BuiltinData{ast::BuiltinValue::kNumWorkgroups, ast::StorageClass::kIn,
+        BuiltinData{ast::BuiltinValue::kWorkgroupId, ast::AddressSpace::kIn, SpvBuiltInWorkgroupId},
+        BuiltinData{ast::BuiltinValue::kNumWorkgroups, ast::AddressSpace::kIn,
                     SpvBuiltInNumWorkgroups},
-        BuiltinData{ast::BuiltinValue::kSampleIndex, ast::StorageClass::kIn, SpvBuiltInSampleId},
-        BuiltinData{ast::BuiltinValue::kSampleMask, ast::StorageClass::kIn, SpvBuiltInSampleMask},
-        BuiltinData{ast::BuiltinValue::kSampleMask, ast::StorageClass::kOut,
+        BuiltinData{ast::BuiltinValue::kSampleIndex, ast::AddressSpace::kIn, SpvBuiltInSampleId},
+        BuiltinData{ast::BuiltinValue::kSampleMask, ast::AddressSpace::kIn, SpvBuiltInSampleMask},
+        BuiltinData{ast::BuiltinValue::kSampleMask, ast::AddressSpace::kOut,
                     SpvBuiltInSampleMask}));
 
 TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
@@ -307,7 +307,7 @@
                                  Member("b", ty.i32()),
                              });
 
-    GlobalVar("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("b", ty.Of(A), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     spirv::Builder& b = SanitizeAndBuild();
@@ -345,7 +345,7 @@
 
     auto* A = Structure("A", utils::Vector{Member("a", ty.i32())});
     auto* B = Alias("B", ty.Of(A));
-    GlobalVar("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("b", ty.Of(B), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     spirv::Builder& b = SanitizeAndBuild();
@@ -381,7 +381,7 @@
 
     auto* A = Structure("A", utils::Vector{Member("a", ty.i32())});
     auto* B = Alias("B", ty.Of(A));
-    GlobalVar("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("b", ty.Of(B), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     spirv::Builder& b = SanitizeAndBuild();
@@ -416,9 +416,9 @@
     // var<storage, read_write> c : A
 
     auto* A = Structure("A", utils::Vector{Member("a", ty.i32())});
-    GlobalVar("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead, Group(0_a),
+    GlobalVar("b", ty.Of(A), ast::AddressSpace::kStorage, ast::Access::kRead, Group(0_a),
               Binding(0_a));
-    GlobalVar("c", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kReadWrite, Group(1_a),
+    GlobalVar("c", ty.Of(A), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(1_a),
               Binding(0_a));
 
     spirv::Builder& b = SanitizeAndBuild();
@@ -473,57 +473,18 @@
 )");
 }
 
-// Check that multiple texture_storage types with different access modifiers
-// only produces a single OpTypeImage.
-// Test disabled as storage textures currently only support 'write' access. In
-// the future we'll likely support read_write.
-TEST_F(BuilderTest, DISABLED_GlobalVar_TextureStorageWithDifferentAccess) {
-    // var<uniform_constant> a : texture_storage_2d<r32uint, read_write>;
-    // var<uniform_constant> b : texture_storage_2d<r32uint, write>;
-
-    auto* type_a = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Uint,
-                                      ast::Access::kReadWrite);
-    auto* var_a = GlobalVar("a", type_a, Binding(0_a), Group(0_a));
-
-    auto* type_b = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Uint,
-                                      ast::Access::kWrite);
-    auto* var_b = GlobalVar("b", type_b, Binding(1_a), Group(0_a));
-
-    spirv::Builder& b = Build();
-
-    EXPECT_TRUE(b.GenerateGlobalVariable(var_a)) << b.error();
-    EXPECT_TRUE(b.GenerateGlobalVariable(var_b)) << b.error();
-
-    EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 NonWritable
-OpDecorate %1 Binding 0
-OpDecorate %1 DescriptorSet 0
-OpDecorate %5 NonReadable
-OpDecorate %5 Binding 1
-OpDecorate %5 DescriptorSet 0
-)");
-    // There must only be one OpTypeImage declaration with the same
-    // arguments
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
-%3 = OpTypeImage %4 2D 0 0 0 2 R32ui
-%2 = OpTypePointer UniformConstant %3
-%1 = OpVariable %2 UniformConstant
-%6 = OpTypePointer UniformConstant %3
-%5 = OpVariable %6 UniformConstant
-)");
-}
-
 TEST_F(BuilderTest, GlobalVar_WorkgroupWithZeroInit) {
     auto* type_scalar = ty.i32();
-    auto* var_scalar = GlobalVar("a", type_scalar, ast::StorageClass::kWorkgroup);
+    auto* var_scalar = GlobalVar("a", type_scalar, ast::AddressSpace::kWorkgroup);
 
     auto* type_array = ty.array<f32, 16>();
-    auto* var_array = GlobalVar("b", type_array, ast::StorageClass::kWorkgroup);
+    auto* var_array = GlobalVar("b", type_array, ast::AddressSpace::kWorkgroup);
 
     auto* type_struct = Structure("C", utils::Vector{
                                            Member("a", ty.i32()),
                                            Member("b", ty.i32()),
                                        });
-    auto* var_struct = GlobalVar("c", ty.Of(type_struct), ast::StorageClass::kWorkgroup);
+    auto* var_struct = GlobalVar("c", ty.Of(type_struct), ast::AddressSpace::kWorkgroup);
 
     program = std::make_unique<Program>(std::move(*this));
 
diff --git a/src/tint/writer/spirv/builder_ident_expression_test.cc b/src/tint/writer/spirv/builder_ident_expression_test.cc
index 0f24146..5d01db3 100644
--- a/src/tint/writer/spirv/builder_ident_expression_test.cc
+++ b/src/tint/writer/spirv/builder_ident_expression_test.cc
@@ -41,7 +41,7 @@
 }
 
 TEST_F(BuilderTest, IdentifierExpression_GlobalVar) {
-    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* expr = Expr("var");
     WrapInFunction(expr);
@@ -85,7 +85,7 @@
 }
 
 TEST_F(BuilderTest, IdentifierExpression_FunctionVar) {
-    auto* v = Var("var", ty.f32(), ast::StorageClass::kFunction);
+    auto* v = Var("var", ty.f32(), ast::AddressSpace::kFunction);
     auto* expr = Expr("var");
     WrapInFunction(v, expr);
 
@@ -109,7 +109,7 @@
 }
 
 TEST_F(BuilderTest, IdentifierExpression_Load) {
-    auto* var = GlobalVar("var", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.i32(), ast::AddressSpace::kPrivate);
     auto* expr = Add("var", "var");
     WrapInFunction(expr);
 
diff --git a/src/tint/writer/spirv/builder_if_test.cc b/src/tint/writer/spirv/builder_if_test.cc
index 6705cce..a83b0cf 100644
--- a/src/tint/writer/spirv/builder_if_test.cc
+++ b/src/tint/writer/spirv/builder_if_test.cc
@@ -68,7 +68,7 @@
     //   v = 2;
     // }
 
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* expr = If(true, body);
     WrapInFunction(expr);
@@ -104,7 +104,7 @@
     //   v = 3i;
     // }
 
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* else_body = Block(Assign("v", 3_i));
 
@@ -146,7 +146,7 @@
     //   v = 3i;
     // }
 
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* else_body = Block(Assign("v", 3_i));
 
@@ -197,7 +197,7 @@
     //   v = 5i;
     // }
 
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* elseif_1_body = Block(Assign("v", 3_i));
     auto* elseif_2_body = Block(Assign("v", 4_i));
@@ -562,7 +562,7 @@
     // if (a) {
     // }
 
-    auto* var = GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("a", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* fn = Func("f", utils::Empty, ty.void_(),
                     utils::Vector{
                         If("a", Block()),
diff --git a/src/tint/writer/spirv/builder_loop_test.cc b/src/tint/writer/spirv/builder_loop_test.cc
index 7e7f327..3ab27ef 100644
--- a/src/tint/writer/spirv/builder_loop_test.cc
+++ b/src/tint/writer/spirv/builder_loop_test.cc
@@ -54,7 +54,7 @@
     //   break;
     // }
 
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* body = Block(Assign("v", 2_i),  //
                        Break());
 
@@ -96,7 +96,7 @@
     //   }
     // }
 
-    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
     auto* body = Block(Assign("v", 2_i),  //
                        Break());
     auto* continuing = Block(Assign("v", 3_i));
diff --git a/src/tint/writer/spirv/builder_switch_test.cc b/src/tint/writer/spirv/builder_switch_test.cc
index c2f526e..4ee8f39 100644
--- a/src/tint/writer/spirv/builder_switch_test.cc
+++ b/src/tint/writer/spirv/builder_switch_test.cc
@@ -57,8 +57,8 @@
     //   default: {}
     // }
 
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -114,8 +114,8 @@
     //   default: {}
     // }
 
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = GlobalVar("a", ty.u32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* a = GlobalVar("a", ty.u32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -171,8 +171,8 @@
     //     v = 1i;
     //  }
 
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -221,8 +221,8 @@
     //      v = 3i;
     //  }
 
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -284,8 +284,8 @@
     //      v = 3i;
     //  }
 
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
@@ -346,8 +346,8 @@
     //   default: {}
     // }
 
-    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* func = Func("a_func", utils::Empty, ty.void_(),
                       utils::Vector{
diff --git a/src/tint/writer/spirv/builder_type_test.cc b/src/tint/writer/spirv/builder_type_test.cc
index 9b61268..a17dcb5 100644
--- a/src/tint/writer/spirv/builder_type_test.cc
+++ b/src/tint/writer/spirv/builder_type_test.cc
@@ -28,7 +28,7 @@
 TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
     auto* ary = ty.array(ty.i32());
     auto* str = Structure("S", utils::Vector{Member("x", ary)});
-    GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("a", ty.Of(str), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     spirv::Builder& b = Build();
@@ -45,7 +45,7 @@
 TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
     auto* ary = ty.array(ty.i32());
     auto* str = Structure("S", utils::Vector{Member("x", ary)});
-    GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead, Binding(0_a),
+    GlobalVar("a", ty.Of(str), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     spirv::Builder& b = Build();
@@ -61,7 +61,7 @@
 
 TEST_F(BuilderTest_Type, GenerateArray) {
     auto* ary = ty.array(ty.i32(), 4_u);
-    GlobalVar("a", ary, ast::StorageClass::kPrivate);
+    GlobalVar("a", ary, ast::AddressSpace::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -78,7 +78,7 @@
 
 TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
     auto* ary = ty.array(ty.i32(), 4_u, 16u);
-    GlobalVar("a", ary, ast::StorageClass::kPrivate);
+    GlobalVar("a", ary, ast::AddressSpace::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -98,7 +98,7 @@
 
 TEST_F(BuilderTest_Type, ReturnsGeneratedArray) {
     auto* ary = ty.array(ty.i32(), 4_u);
-    GlobalVar("a", ary, ast::StorageClass::kPrivate);
+    GlobalVar("a", ary, ast::AddressSpace::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -293,7 +293,7 @@
 
 TEST_F(BuilderTest_Type, GeneratePtr) {
     auto* i32 = create<sem::I32>();
-    auto* ptr = create<sem::Pointer>(i32, ast::StorageClass::kOut, ast::Access::kReadWrite);
+    auto* ptr = create<sem::Pointer>(i32, ast::AddressSpace::kOut, ast::Access::kReadWrite);
 
     spirv::Builder& b = Build();
 
@@ -308,7 +308,7 @@
 
 TEST_F(BuilderTest_Type, ReturnsGeneratedPtr) {
     auto* i32 = create<sem::I32>();
-    auto* ptr = create<sem::Pointer>(i32, ast::StorageClass::kOut, ast::Access::kReadWrite);
+    auto* ptr = create<sem::Pointer>(i32, ast::AddressSpace::kOut, ast::Access::kReadWrite);
 
     spirv::Builder& b = Build();
 
@@ -336,7 +336,7 @@
 TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers) {
     auto* s = Structure("S", utils::Vector{
                                  Member("a", ty.f32()),
-                                 Member("b", ty.f32(), utils::Vector{MemberAlign(8_u)}),
+                                 Member("b", ty.f32(), utils::Vector{MemberAlign(8_i)}),
                              });
 
     spirv::Builder& b = Build();
@@ -576,7 +576,7 @@
 }
 
 struct PtrData {
-    ast::StorageClass ast_class;
+    ast::AddressSpace ast_class;
     SpvStorageClass result;
 };
 inline std::ostream& operator<<(std::ostream& out, PtrData data) {
@@ -584,25 +584,25 @@
     return out;
 }
 using PtrDataTest = TestParamHelper<PtrData>;
-TEST_P(PtrDataTest, ConvertStorageClass) {
+TEST_P(PtrDataTest, ConvertAddressSpace) {
     auto params = GetParam();
 
     spirv::Builder& b = Build();
 
-    EXPECT_EQ(b.ConvertStorageClass(params.ast_class), params.result);
+    EXPECT_EQ(b.ConvertAddressSpace(params.ast_class), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
     BuilderTest_Type,
     PtrDataTest,
-    testing::Values(PtrData{ast::StorageClass::kNone, SpvStorageClassMax},
-                    PtrData{ast::StorageClass::kIn, SpvStorageClassInput},
-                    PtrData{ast::StorageClass::kOut, SpvStorageClassOutput},
-                    PtrData{ast::StorageClass::kUniform, SpvStorageClassUniform},
-                    PtrData{ast::StorageClass::kWorkgroup, SpvStorageClassWorkgroup},
-                    PtrData{ast::StorageClass::kHandle, SpvStorageClassUniformConstant},
-                    PtrData{ast::StorageClass::kStorage, SpvStorageClassStorageBuffer},
-                    PtrData{ast::StorageClass::kPrivate, SpvStorageClassPrivate},
-                    PtrData{ast::StorageClass::kFunction, SpvStorageClassFunction}));
+    testing::Values(PtrData{ast::AddressSpace::kNone, SpvStorageClassMax},
+                    PtrData{ast::AddressSpace::kIn, SpvStorageClassInput},
+                    PtrData{ast::AddressSpace::kOut, SpvStorageClassOutput},
+                    PtrData{ast::AddressSpace::kUniform, SpvStorageClassUniform},
+                    PtrData{ast::AddressSpace::kWorkgroup, SpvStorageClassWorkgroup},
+                    PtrData{ast::AddressSpace::kHandle, SpvStorageClassUniformConstant},
+                    PtrData{ast::AddressSpace::kStorage, SpvStorageClassStorageBuffer},
+                    PtrData{ast::AddressSpace::kPrivate, SpvStorageClassPrivate},
+                    PtrData{ast::AddressSpace::kFunction, SpvStorageClassFunction}));
 
 TEST_F(BuilderTest_Type, DepthTexture_Generate_2d) {
     auto* two_d = create<sem::DepthTexture>(ast::TextureDimension::k2d);
diff --git a/src/tint/writer/spirv/generator_impl.h b/src/tint/writer/spirv/generator_impl.h
index 106e2f8..c83f144 100644
--- a/src/tint/writer/spirv/generator_impl.h
+++ b/src/tint/writer/spirv/generator_impl.h
@@ -42,7 +42,7 @@
     /// Constructor
     /// @param program the program to generate
     /// @param zero_initialize_workgroup_memory `true` to initialize all the
-    /// variables in the Workgroup storage class with OpConstantNull
+    /// variables in the Workgroup address space with OpConstantNull
     GeneratorImpl(const Program* program, bool zero_initialize_workgroup_memory);
 
     /// @returns true on successful generation; false otherwise
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 83a96d9..05f1a5a 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -429,7 +429,7 @@
             return true;
         },
         [&](const ast::Pointer* ptr) {
-            out << "ptr<" << ptr->storage_class << ", ";
+            out << "ptr<" << ptr->address_space << ", ";
             if (!EmitType(out, ptr->type)) {
                 return false;
             }
@@ -654,10 +654,10 @@
         v,  //
         [&](const ast::Var* var) {
             out << "var";
-            auto sc = var->declared_storage_class;
+            auto address_space = var->declared_address_space;
             auto ac = var->declared_access;
-            if (sc != ast::StorageClass::kNone || ac != ast::Access::kUndefined) {
-                out << "<" << sc;
+            if (address_space != ast::AddressSpace::kNone || ac != ast::Access::kUndefined) {
+                out << "<" << address_space;
                 if (ac != ast::Access::kUndefined) {
                     out << ", ";
                     if (!EmitAccess(out, ac)) {
diff --git a/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc b/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
index 0b28ae9..652f598 100644
--- a/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, IndexAccessor) {
-    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::AddressSpace::kPrivate);
     auto* expr = IndexAccessor("ary", 5_i);
     WrapInFunction(expr);
 
@@ -34,7 +34,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, IndexAccessor_OfDref) {
-    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::AddressSpace::kPrivate);
 
     auto* p = Let("p", AddressOf("ary"));
     auto* expr = IndexAccessor(Deref("p"), 5_i);
diff --git a/src/tint/writer/wgsl/generator_impl_assign_test.cc b/src/tint/writer/wgsl/generator_impl_assign_test.cc
index e390d2f..85d4d90 100644
--- a/src/tint/writer/wgsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_assign_test.cc
@@ -20,8 +20,8 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Assign) {
-    auto* lhs = GlobalVar("lhs", ty.i32(), ast::StorageClass::kPrivate);
-    auto* rhs = GlobalVar("rhs", ty.i32(), ast::StorageClass::kPrivate);
+    auto* lhs = GlobalVar("lhs", ty.i32(), ast::AddressSpace::kPrivate);
+    auto* rhs = GlobalVar("rhs", ty.i32(), ast::AddressSpace::kPrivate);
     auto* assign = Assign(lhs, rhs);
     WrapInFunction(assign);
 
diff --git a/src/tint/writer/wgsl/generator_impl_binary_test.cc b/src/tint/writer/wgsl/generator_impl_binary_test.cc
index 682c001..d069fe6 100644
--- a/src/tint/writer/wgsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_binary_test.cc
@@ -37,8 +37,8 @@
         }
     };
 
-    GlobalVar("left", op_ty(), ast::StorageClass::kPrivate);
-    GlobalVar("right", op_ty(), ast::StorageClass::kPrivate);
+    GlobalVar("left", op_ty(), ast::AddressSpace::kPrivate);
+    GlobalVar("right", op_ty(), ast::AddressSpace::kPrivate);
     auto* left = Expr("left");
     auto* right = Expr("right");
 
diff --git a/src/tint/writer/wgsl/generator_impl_call_test.cc b/src/tint/writer/wgsl/generator_impl_call_test.cc
index 4746be2..6b22d2a 100644
--- a/src/tint/writer/wgsl/generator_impl_call_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_call_test.cc
@@ -48,8 +48,8 @@
          utils::Vector{
              Return(1.23_f),
          });
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -68,8 +68,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), utils::Empty, utils::Empty);
-    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
-    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::AddressSpace::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     auto* stmt = CallStmt(call);
diff --git a/src/tint/writer/wgsl/generator_impl_function_test.cc b/src/tint/writer/wgsl/generator_impl_function_test.cc
index af09a13..e785520 100644
--- a/src/tint/writer/wgsl/generator_impl_function_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_function_test.cc
@@ -179,7 +179,7 @@
                                     Member("d", ty.f32()),
                                 });
 
-    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, Binding(0_a),
+    GlobalVar("data", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Binding(0_a),
               Group(0_a));
 
     {
diff --git a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
index bc77623..db45e79 100644
--- a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
@@ -28,7 +28,7 @@
     auto* func_var = Var("a", ty.f32());
     WrapInFunction(func_var);
 
-    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -45,7 +45,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_GlobalsInterleaved) {
-    GlobalVar("a0", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a0", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* s0 = Structure("S0", utils::Vector{
                                    Member("a", ty.i32()),
@@ -57,7 +57,7 @@
          },
          utils::Empty);
 
-    GlobalVar("a1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a1", ty.f32(), ast::AddressSpace::kPrivate);
 
     auto* s1 = Structure("S1", utils::Vector{
                                    Member("a", ty.i32()),
diff --git a/src/tint/writer/wgsl/generator_impl_identifier_test.cc b/src/tint/writer/wgsl/generator_impl_identifier_test.cc
index 2e20d4c..25e2a7a 100644
--- a/src/tint/writer/wgsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_identifier_test.cc
@@ -20,7 +20,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_Single) {
-    GlobalVar("glsl", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("glsl", ty.f32(), ast::AddressSpace::kPrivate);
     auto* i = Expr("glsl");
     WrapInFunction(i);
 
diff --git a/src/tint/writer/wgsl/generator_impl_if_test.cc b/src/tint/writer/wgsl/generator_impl_if_test.cc
index 88f6aba..eeaa75f 100644
--- a/src/tint/writer/wgsl/generator_impl_if_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_if_test.cc
@@ -20,7 +20,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_If) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* cond = Expr("cond");
     auto* body = Block(Return());
@@ -39,8 +39,8 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithElseIf) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_cond = Expr("else_cond");
     auto* else_body = Block(Return());
@@ -64,7 +64,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithElse) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_body = Block(Return());
 
@@ -87,8 +87,8 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithMultiple) {
-    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::AddressSpace::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::AddressSpace::kPrivate);
 
     auto* else_cond = Expr("else_cond");
 
diff --git a/src/tint/writer/wgsl/generator_impl_literal_test.cc b/src/tint/writer/wgsl/generator_impl_literal_test.cc
index f33bd0e..f96b09e 100644
--- a/src/tint/writer/wgsl/generator_impl_literal_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_literal_test.cc
@@ -152,34 +152,6 @@
                              {MakeF32(0, 255, 0), "0x1p+128f"},
                              {MakeF32(1, 255, 0), "-0x1p+128f"}}));
 
-INSTANTIATE_TEST_SUITE_P(
-    // TODO(dneto): It's unclear how Infinity and NaN should be handled.
-    // https://github.com/gpuweb/gpuweb/issues/1769
-    // This test fails on Windows x86-64 because the machine sets the high
-    // mantissa bit on NaNs.
-    DISABLED_NaN,
-    // In the NaN case, the top bit in the mantissa is often used to encode
-    // whether the NaN is signalling or quiet, but no agreement between
-    // different machine architectures on whether 1 means signalling or
-    // if 1 means quiet.
-    WgslGenerator_F32LiteralTest,
-    ::testing::ValuesIn(std::vector<F32Data>{
-        // LSB only.  Smallest mantissa.
-        {MakeF32(0, 255, 1), "0x1.000002p+128f"},  // Smallest mantissa
-        {MakeF32(1, 255, 1), "-0x1.000002p+128f"},
-        // MSB only.
-        {MakeF32(0, 255, 0x400000), "0x1.8p+128f"},
-        {MakeF32(1, 255, 0x400000), "-0x1.8p+128f"},
-        // All 1s in the mantissa.
-        {MakeF32(0, 255, 0x7fffff), "0x1.fffffep+128f"},
-        {MakeF32(1, 255, 0x7fffff), "-0x1.fffffep+128f"},
-        // Scattered bits, with 0 in top mantissa bit.
-        {MakeF32(0, 255, 0x20101f), "0x1.40203ep+128f"},
-        {MakeF32(1, 255, 0x20101f), "-0x1.40203ep+128f"},
-        // Scattered bits, with 1 in top mantissa bit.
-        {MakeF32(0, 255, 0x40101f), "0x1.80203ep+128f"},
-        {MakeF32(1, 255, 0x40101f), "-0x1.80203ep+128f"}}));
-
 using WgslGenerator_F16LiteralTest = TestParamHelper<F16Data>;
 
 TEST_P(WgslGenerator_F16LiteralTest, Emit) {
@@ -222,35 +194,5 @@
                              {MakeF16(1, 0, 0x2c7u), "-4.23789024e-05h"},  // Scattered bits
                          }));
 
-INSTANTIATE_TEST_SUITE_P(
-    // Currently Inf is impossible to be spelled out in literal.
-    // https://github.com/gpuweb/gpuweb/issues/1769
-    DISABLED_Infinity,
-    WgslGenerator_F16LiteralTest,
-    ::testing::ValuesIn(std::vector<F16Data>{{MakeF16(0, 31, 0), "0x1p+128h"},
-                                             {MakeF16(1, 31, 0), "-0x1p+128h"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    // Currently NaN is impossible to be spelled out in literal.
-    // https://github.com/gpuweb/gpuweb/issues/1769
-    DISABLED_NaN,
-    WgslGenerator_F16LiteralTest,
-    ::testing::ValuesIn(std::vector<F16Data>{
-        // LSB only.  Smallest mantissa.
-        {MakeF16(0, 31, 1), "0x1.004p+128h"},  // Smallest mantissa
-        {MakeF16(1, 31, 1), "-0x1.004p+128h"},
-        // MSB only.
-        {MakeF16(0, 31, 0x200u), "0x1.8p+128h"},
-        {MakeF16(1, 31, 0x200u), "-0x1.8p+128h"},
-        // All 1s in the mantissa.
-        {MakeF16(0, 31, 0x3ffu), "0x1.ffcp+128h"},
-        {MakeF16(1, 31, 0x3ffu), "-0x1.ffcp+128h"},
-        // Scattered bits, with 0 in top mantissa bit.
-        {MakeF16(0, 31, 0x11fu), "0x1.47cp+128h"},
-        {MakeF16(1, 31, 0x11fu), "-0x1.47cp+128h"},
-        // Scattered bits, with 1 in top mantissa bit.
-        {MakeF16(0, 31, 0x23fu), "0x1.8fcp+128h"},
-        {MakeF16(1, 31, 0x23fu), "-0x1.8fcp+128h"}}));
-
 }  // namespace
 }  // namespace tint::writer::wgsl
diff --git a/src/tint/writer/wgsl/generator_impl_loop_test.cc b/src/tint/writer/wgsl/generator_impl_loop_test.cc
index 3570957..f4b6898 100644
--- a/src/tint/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_loop_test.cc
@@ -68,7 +68,7 @@
     // for({ignore(1i); ignore(2i);}; ; ) {
     //   return;
     // }
-    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::AddressSpace::kWorkgroup);
     auto* multi_stmt = Block(Ignore(1_i), Ignore(2_i));
     auto* f = For(multi_stmt, nullptr, nullptr, Block(Return()));
     WrapInFunction(f);
@@ -132,7 +132,7 @@
     //   return;
     // }
 
-    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::AddressSpace::kWorkgroup);
     auto* multi_stmt = Block(Ignore(1_i), Ignore(2_i));
     auto* f = For(nullptr, nullptr, multi_stmt, Block(Return()));
     WrapInFunction(f);
@@ -175,7 +175,7 @@
     // for({ ignore(1i); ignore(2i); }; true; { ignore(3i); ignore(4i); }) {
     //   return;
     // }
-    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::AddressSpace::kWorkgroup);
     auto* multi_stmt_a = Block(Ignore(1_i), Ignore(2_i));
     auto* multi_stmt_b = Block(Ignore(3_i), Ignore(4_i));
     auto* f = For(multi_stmt_a, Expr(true), multi_stmt_b, Block(Return()));
diff --git a/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc b/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
index 849f018..9bb695d 100644
--- a/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
@@ -21,7 +21,7 @@
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_MemberAccessor) {
     auto* s = Structure("Data", utils::Vector{Member("mem", ty.f32())});
-    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::AddressSpace::kPrivate);
 
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(expr);
@@ -35,7 +35,7 @@
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_MemberAccessor_OfDref) {
     auto* s = Structure("Data", utils::Vector{Member("mem", ty.f32())});
-    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::AddressSpace::kPrivate);
 
     auto* p = Let("p", AddressOf("str"));
     auto* expr = MemberAccessor(Deref("p"), "mem");
diff --git a/src/tint/writer/wgsl/generator_impl_switch_test.cc b/src/tint/writer/wgsl/generator_impl_switch_test.cc
index 491660e..5cf0ed2 100644
--- a/src/tint/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_switch_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Switch) {
-    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::AddressSpace::kPrivate);
 
     auto* def_body = Block(create<ast::BreakStatement>());
     auto* def = create<ast::CaseStatement>(utils::Empty, def_body);
diff --git a/src/tint/writer/wgsl/generator_impl_type_test.cc b/src/tint/writer/wgsl/generator_impl_type_test.cc
index f6f39d4..13c8411 100644
--- a/src/tint/writer/wgsl/generator_impl_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_type_test.cc
@@ -140,7 +140,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_Pointer) {
-    auto* p = ty.pointer<f32>(ast::StorageClass::kWorkgroup);
+    auto* p = ty.pointer<f32>(ast::AddressSpace::kWorkgroup);
     Alias("make_type_reachable", p);
 
     GeneratorImpl& gen = Build();
@@ -151,7 +151,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_PointerAccessMode) {
-    auto* p = ty.pointer<f32>(ast::StorageClass::kStorage, ast::Access::kReadWrite);
+    auto* p = ty.pointer<f32>(ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     Alias("make_type_reachable", p);
 
     GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/wgsl/generator_impl_unary_op_test.cc b/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
index dadcede..0d5edd5 100644
--- a/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using WgslUnaryOpTest = TestHelper;
 
 TEST_F(WgslUnaryOpTest, AddressOf) {
-    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Complement) {
-    GlobalVar("expr", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.u32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Indirection) {
-    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::AddressSpace::kPrivate);
     auto* p = Let("expr", create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
     WrapInFunction(p, op);
@@ -57,7 +57,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Not) {
-    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -69,7 +69,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Negation) {
-    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::AddressSpace::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/wgsl/generator_impl_variable_test.cc b/src/tint/writer/wgsl/generator_impl_variable_test.cc
index f1ac216..b7c894e 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitVariable) {
-    auto* v = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -31,8 +31,8 @@
     EXPECT_EQ(out.str(), R"(var<private> a : f32;)");
 }
 
-TEST_F(WgslGeneratorImplTest, EmitVariable_StorageClass) {
-    auto* v = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+TEST_F(WgslGeneratorImplTest, EmitVariable_AddressSpace) {
+    auto* v = GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -43,7 +43,7 @@
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Read) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.i32())});
-    auto* v = GlobalVar("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+    auto* v = GlobalVar("a", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
                         Binding(0_a), Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -55,7 +55,7 @@
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_ReadWrite) {
     auto* s = Structure("S", utils::Vector{Member("a", ty.i32())});
-    auto* v = GlobalVar("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+    auto* v = GlobalVar("a", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
                         Binding(0_a), Group(0_a));
 
     GeneratorImpl& gen = Build();
@@ -76,7 +76,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Constructor) {
-    auto* v = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr(1_f));
+    auto* v = GlobalVar("a", ty.f32(), ast::AddressSpace::kPrivate, Expr(1_f));
 
     GeneratorImpl& gen = Build();