tint/resolver: Resolve builtin enumerators

'address space', 'access' and 'texel_format'

Bug: tint:1810
Change-Id: If471912fcef57f7579d76c8c4edd663d1c0311ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/119125
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f08b4ee..574b275 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -363,6 +363,7 @@
     "sem/block_statement.h",
     "sem/break_if_statement.h",
     "sem/builtin.h",
+    "sem/builtin_enum_expression.h",
     "sem/builtin_type.h",
     "sem/call.h",
     "sem/call_target.h",
@@ -755,6 +756,8 @@
     "sem/break_if_statement.h",
     "sem/builtin.cc",
     "sem/builtin.h",
+    "sem/builtin_enum_expression.cc",
+    "sem/builtin_enum_expression.h",
     "sem/builtin_type.cc",
     "sem/builtin_type.h",
     "sem/call.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 46aa1c4..473e0ee 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -325,6 +325,8 @@
   sem/break_if_statement.h
   sem/builtin.cc
   sem/builtin.h
+  sem/builtin_enum_expression.cc
+  sem/builtin_enum_expression.h
   sem/call_target.cc
   sem/call_target.h
   sem/call.cc
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index b77c812..61bb760 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -488,6 +488,18 @@
                 graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_ty));
                 return;
             }
+            if (auto addr = type::ParseAddressSpace(s); addr != type::AddressSpace::kUndefined) {
+                graph_.resolved_identifiers.Add(from, ResolvedIdentifier(addr));
+                return;
+            }
+            if (auto fmt = type::ParseTexelFormat(s); fmt != type::TexelFormat::kUndefined) {
+                graph_.resolved_identifiers.Add(from, ResolvedIdentifier(fmt));
+                return;
+            }
+            if (auto access = type::ParseAccess(s); access != type::Access::kUndefined) {
+                graph_.resolved_identifiers.Add(from, ResolvedIdentifier(access));
+                return;
+            }
 
             UnknownSymbol(to, from->source, use);
             return;
@@ -859,6 +871,15 @@
     if (auto builtin_ty = BuiltinType(); builtin_ty != type::Builtin::kUndefined) {
         return "builtin type '" + utils::ToString(builtin_ty) + "'";
     }
+    if (auto access = Access(); access != type::Access::kUndefined) {
+        return "access '" + utils::ToString(access) + "'";
+    }
+    if (auto addr = AddressSpace(); addr != type::AddressSpace::kUndefined) {
+        return "address space '" + utils::ToString(addr) + "'";
+    }
+    if (auto fmt = TexelFormat(); fmt != type::TexelFormat::kUndefined) {
+        return "texel format '" + utils::ToString(fmt) + "'";
+    }
     TINT_UNREACHABLE(Resolver, diagnostics) << "unhandled ResolvedIdentifier";
     return "<unknown>";
 }
diff --git a/src/tint/resolver/dependency_graph.h b/src/tint/resolver/dependency_graph.h
index dc26289..8b2e668 100644
--- a/src/tint/resolver/dependency_graph.h
+++ b/src/tint/resolver/dependency_graph.h
@@ -21,7 +21,9 @@
 #include "src/tint/ast/module.h"
 #include "src/tint/diagnostic/diagnostic.h"
 #include "src/tint/sem/builtin_type.h"
+#include "src/tint/type/access.h"
 #include "src/tint/type/builtin.h"
+#include "src/tint/type/texel_format.h"
 #include "src/tint/utils/hashmap.h"
 
 namespace tint::resolver {
@@ -32,7 +34,10 @@
 /// - const ast::Variable*  (as const ast::Node*)
 /// - const ast::Function*  (as const ast::Node*)
 /// - sem::BuiltinType
+/// - type::Access
+/// - type::AddressSpace
 /// - type::Builtin
+/// - type::TexelFormat
 class ResolvedIdentifier {
   public:
     ResolvedIdentifier() = default;
@@ -62,6 +67,24 @@
         return sem::BuiltinType::kNone;
     }
 
+    /// @return the access if the ResolvedIdentifier holds type::Access, otherwise
+    /// type::Access::kUndefined
+    type::Access Access() const {
+        if (auto n = std::get_if<type::Access>(&value_)) {
+            return *n;
+        }
+        return type::Access::kUndefined;
+    }
+
+    /// @return the address space if the ResolvedIdentifier holds type::AddressSpace, otherwise
+    /// type::AddressSpace::kUndefined
+    type::AddressSpace AddressSpace() const {
+        if (auto n = std::get_if<type::AddressSpace>(&value_)) {
+            return *n;
+        }
+        return type::AddressSpace::kUndefined;
+    }
+
     /// @return the builtin type if the ResolvedIdentifier holds type::Builtin, otherwise
     /// type::Builtin::kUndefined
     type::Builtin BuiltinType() const {
@@ -71,6 +94,15 @@
         return type::Builtin::kUndefined;
     }
 
+    /// @return the texel format if the ResolvedIdentifier holds type::TexelFormat, otherwise
+    /// type::TexelFormat::kUndefined
+    type::TexelFormat TexelFormat() const {
+        if (auto n = std::get_if<type::TexelFormat>(&value_)) {
+            return *n;
+        }
+        return type::TexelFormat::kUndefined;
+    }
+
     /// @param value the value to compare the ResolvedIdentifier to
     /// @return true if the ResolvedIdentifier is equal to @p value
     template <typename T>
@@ -94,7 +126,14 @@
     std::string String(const SymbolTable& symbols, diag::List& diagnostics) const;
 
   private:
-    std::variant<std::monostate, const ast::Node*, sem::BuiltinType, type::Builtin> value_;
+    std::variant<std::monostate,
+                 const ast::Node*,
+                 sem::BuiltinType,
+                 type::Access,
+                 type::AddressSpace,
+                 type::Builtin,
+                 type::TexelFormat>
+        value_;
 };
 
 /// DependencyGraph holds information about module-scope declaration dependency
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 57e84f1..2953ade 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1325,6 +1325,261 @@
 }  // namespace resolve_to_builtin_type
 
 ////////////////////////////////////////////////////////////////////////////////
+// Resolve to type::Access tests
+////////////////////////////////////////////////////////////////////////////////
+namespace resolve_to_access {
+
+using ResolverDependencyGraphResolveToAccess =
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+
+TEST_P(ResolverDependencyGraphResolveToAccess, Resolve) {
+    const auto use = std::get<0>(GetParam());
+    const auto name = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(name);
+
+    SymbolTestHelper helper(this);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Access(), type::ParseAccess(name))
+        << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToAccess, ShadowedByGlobalVar) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::GlobalVar, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToAccess, ShadowedByStruct) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::Struct, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToAccess, ShadowedByFunc) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::Function, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+INSTANTIATE_TEST_SUITE_P(Types,
+                         ResolverDependencyGraphResolveToAccess,
+                         testing::Combine(testing::ValuesIn(kTypeUseKinds),
+                                          testing::ValuesIn(type::kAccessStrings)));
+
+INSTANTIATE_TEST_SUITE_P(Values,
+                         ResolverDependencyGraphResolveToAccess,
+                         testing::Combine(testing::ValuesIn(kValueUseKinds),
+                                          testing::ValuesIn(type::kAccessStrings)));
+
+INSTANTIATE_TEST_SUITE_P(Functions,
+                         ResolverDependencyGraphResolveToAccess,
+                         testing::Combine(testing::ValuesIn(kFuncUseKinds),
+                                          testing::ValuesIn(type::kAccessStrings)));
+
+}  // namespace resolve_to_access
+
+////////////////////////////////////////////////////////////////////////////////
+// Resolve to type::AddressSpace tests
+////////////////////////////////////////////////////////////////////////////////
+namespace resolve_to_address_space {
+
+using ResolverDependencyGraphResolveToAddressSpace =
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+
+TEST_P(ResolverDependencyGraphResolveToAddressSpace, Resolve) {
+    const auto use = std::get<0>(GetParam());
+    const auto name = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(name);
+
+    SymbolTestHelper helper(this);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->AddressSpace(), type::ParseAddressSpace(name))
+        << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToAddressSpace, ShadowedByGlobalVar) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::GlobalVar, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToAddressSpace, ShadowedByStruct) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::Struct, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToAddressSpace, ShadowedByFunc) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::Function, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+INSTANTIATE_TEST_SUITE_P(Types,
+                         ResolverDependencyGraphResolveToAddressSpace,
+                         testing::Combine(testing::ValuesIn(kTypeUseKinds),
+                                          testing::ValuesIn(type::kAddressSpaceStrings)));
+
+INSTANTIATE_TEST_SUITE_P(Values,
+                         ResolverDependencyGraphResolveToAddressSpace,
+                         testing::Combine(testing::ValuesIn(kValueUseKinds),
+                                          testing::ValuesIn(type::kAddressSpaceStrings)));
+
+INSTANTIATE_TEST_SUITE_P(Functions,
+                         ResolverDependencyGraphResolveToAddressSpace,
+                         testing::Combine(testing::ValuesIn(kFuncUseKinds),
+                                          testing::ValuesIn(type::kAddressSpaceStrings)));
+
+}  // namespace resolve_to_address_space
+
+////////////////////////////////////////////////////////////////////////////////
+// Resolve to type::TexelFormat tests
+////////////////////////////////////////////////////////////////////////////////
+namespace resolve_to_texel_format {
+
+using ResolverDependencyGraphResolveToTexelFormat =
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+
+TEST_P(ResolverDependencyGraphResolveToTexelFormat, Resolve) {
+    const auto use = std::get<0>(GetParam());
+    const auto name = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(name);
+
+    SymbolTestHelper helper(this);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->TexelFormat(), type::ParseTexelFormat(name))
+        << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToTexelFormat, ShadowedByGlobalVar) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::GlobalVar, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToTexelFormat, ShadowedByStruct) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::Struct, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+TEST_P(ResolverDependencyGraphResolveToTexelFormat, ShadowedByFunc) {
+    const auto use = std::get<0>(GetParam());
+    const auto builtin = std::get<1>(GetParam());
+    const auto symbol = Symbols().New(utils::ToString(builtin));
+
+    SymbolTestHelper helper(this);
+    auto* decl = helper.Add(SymbolDeclKind::Function, symbol);
+    auto* ident = helper.Add(use, symbol);
+    helper.Build();
+
+    auto resolved = Build().resolved_identifiers.Get(ident);
+    ASSERT_TRUE(resolved);
+    EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
+}
+
+INSTANTIATE_TEST_SUITE_P(Types,
+                         ResolverDependencyGraphResolveToTexelFormat,
+                         testing::Combine(testing::ValuesIn(kTypeUseKinds),
+                                          testing::ValuesIn(type::kTexelFormatStrings)));
+
+INSTANTIATE_TEST_SUITE_P(Values,
+                         ResolverDependencyGraphResolveToTexelFormat,
+                         testing::Combine(testing::ValuesIn(kValueUseKinds),
+                                          testing::ValuesIn(type::kTexelFormatStrings)));
+
+INSTANTIATE_TEST_SUITE_P(Functions,
+                         ResolverDependencyGraphResolveToTexelFormat,
+                         testing::Combine(testing::ValuesIn(kFuncUseKinds),
+                                          testing::ValuesIn(type::kTexelFormatStrings)));
+
+}  // namespace resolve_to_texel_format
+
+////////////////////////////////////////////////////////////////////////////////
 // Shadowing tests
 ////////////////////////////////////////////////////////////////////////////////
 namespace shadowing {
diff --git a/src/tint/resolver/expression_kind_test.cc b/src/tint/resolver/expression_kind_test.cc
index e662570..3a8568d 100644
--- a/src/tint/resolver/expression_kind_test.cc
+++ b/src/tint/resolver/expression_kind_test.cc
@@ -23,16 +23,23 @@
 namespace {
 
 enum class Def {
+    kAccess,
+    kAddressSpace,
     kBuiltinFunction,
     kBuiltinType,
     kFunction,
     kStruct,
+    kTexelFormat,
     kTypeAlias,
     kVariable,
 };
 
 std::ostream& operator<<(std::ostream& out, Def def) {
     switch (def) {
+        case Def::kAccess:
+            return out << "Def::kAccess";
+        case Def::kAddressSpace:
+            return out << "Def::kAddressSpace";
         case Def::kBuiltinFunction:
             return out << "Def::kBuiltinFunction";
         case Def::kBuiltinType:
@@ -41,6 +48,8 @@
             return out << "Def::kFunction";
         case Def::kStruct:
             return out << "Def::kStruct";
+        case Def::kTexelFormat:
+            return out << "Def::kTexelFormat";
         case Def::kTypeAlias:
             return out << "Def::kTypeAlias";
         case Def::kVariable:
@@ -50,16 +59,23 @@
 }
 
 enum class Use {
+    kAccess,
+    kAddressSpace,
     kCallExpr,
     kCallStmt,
     kFunctionReturnType,
     kMemberType,
+    kTexelFormat,
     kValueExpression,
     kVariableType,
 };
 
 std::ostream& operator<<(std::ostream& out, Use use) {
     switch (use) {
+        case Use::kAccess:
+            return out << "Use::kAccess";
+        case Use::kAddressSpace:
+            return out << "Use::kAddressSpace";
         case Use::kCallExpr:
             return out << "Use::kCallExpr";
         case Use::kCallStmt:
@@ -68,6 +84,8 @@
             return out << "Use::kFunctionReturnType";
         case Use::kMemberType:
             return out << "Use::kMemberType";
+        case Use::kTexelFormat:
+            return out << "Use::kTexelFormat";
         case Use::kValueExpression:
             return out << "Use::kValueExpression";
         case Use::kVariableType:
@@ -96,6 +114,12 @@
 TEST_P(ResolverExpressionKindTest, Test) {
     Symbol sym;
     switch (GetParam().def) {
+        case Def::kAccess:
+            sym = Sym("read_write");
+            break;
+        case Def::kAddressSpace:
+            sym = Sym("workgroup");
+            break;
         case Def::kBuiltinFunction:
             sym = Sym("workgroupBarrier");
             break;
@@ -110,6 +134,9 @@
             Structure(kDefSource, "STRUCT", utils::Vector{Member("m", ty.i32())});
             sym = Sym("STRUCT");
             break;
+        case Def::kTexelFormat:
+            sym = Sym("rgba8unorm");
+            break;
         case Def::kTypeAlias:
             Alias(kDefSource, "ALIAS", ty.i32());
             sym = Sym("ALIAS");
@@ -121,6 +148,10 @@
     }
 
     switch (GetParam().use) {
+        case Use::kAccess:
+            return;  // TODO(crbug.com/tint/1810)
+        case Use::kAddressSpace:
+            return;  // TODO(crbug.com/tint/1810)
         case Use::kCallExpr:
             Func("f", utils::Empty, ty.void_(), Decl(Var("v", Call(Ident(kUseSource, sym)))));
             break;
@@ -133,6 +164,8 @@
         case Use::kMemberType:
             Structure("s", utils::Vector{Member("m", ty(kUseSource, sym))});
             break;
+        case Use::kTexelFormat:
+            return;  // TODO(crbug.com/tint/1810)
         case Use::kValueExpression:
             GlobalVar("v", type::AddressSpace::kPrivate, Expr(kUseSource, sym));
             break;
@@ -154,26 +187,64 @@
     ,
     ResolverExpressionKindTest,
     testing::ValuesIn(std::vector<Case>{
+        {Def::kAccess, Use::kAccess, kPass},
+        {Def::kAccess, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kAccess, Use::kCallExpr,
+         R"(5:6 error: cannot use access 'read_write' as call target)"},
+        {Def::kAccess, Use::kCallStmt,
+         R"(5:6 error: cannot use access 'read_write' as call target)"},
+        {Def::kAccess, Use::kFunctionReturnType,
+         R"(5:6 error: cannot use access 'read_write' as type)"},
+        {Def::kAccess, Use::kMemberType, R"(5:6 error: cannot use access 'read_write' as type)"},
+        {Def::kAccess, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kAccess, Use::kValueExpression,
+         R"(5:6 error: cannot use access 'read_write' as value)"},
+        {Def::kAccess, Use::kVariableType, R"(5:6 error: cannot use access 'read_write' as type)"},
+
+        {Def::kAddressSpace, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kAddressSpace, Use::kAddressSpace, kPass},
+        {Def::kAddressSpace, Use::kCallExpr,
+         R"(5:6 error: cannot use address space 'workgroup' as call target)"},
+        {Def::kAddressSpace, Use::kCallStmt,
+         R"(5:6 error: cannot use address space 'workgroup' as call target)"},
+        {Def::kAddressSpace, Use::kFunctionReturnType,
+         R"(5:6 error: cannot use address space 'workgroup' as type)"},
+        {Def::kAddressSpace, Use::kMemberType,
+         R"(5:6 error: cannot use address space 'workgroup' as type)"},
+        {Def::kAddressSpace, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kAddressSpace, Use::kValueExpression,
+         R"(5:6 error: cannot use address space 'workgroup' as value)"},
+        {Def::kAddressSpace, Use::kVariableType,
+         R"(5:6 error: cannot use address space 'workgroup' as type)"},
+
+        {Def::kBuiltinFunction, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kBuiltinFunction, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
         {Def::kBuiltinFunction, Use::kCallStmt, kPass},
         {Def::kBuiltinFunction, Use::kFunctionReturnType,
          R"(5:6 error: cannot use builtin function 'workgroupBarrier' as type)"},
         {Def::kBuiltinFunction, Use::kMemberType,
          R"(5:6 error: cannot use builtin function 'workgroupBarrier' as type)"},
+        {Def::kBuiltinFunction, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
         {Def::kBuiltinFunction, Use::kValueExpression,
          R"(7:8 error: missing '(' for builtin function call)"},
         {Def::kBuiltinFunction, Use::kVariableType,
          R"(5:6 error: cannot use builtin function 'workgroupBarrier' as type)"},
 
+        {Def::kBuiltinType, Use::kAccess, kPass},
+        {Def::kBuiltinType, Use::kAddressSpace, kPass},
         {Def::kBuiltinType, Use::kCallExpr, kPass},
         {Def::kBuiltinType, Use::kCallStmt, kPass},
         {Def::kBuiltinType, Use::kFunctionReturnType, kPass},
         {Def::kBuiltinType, Use::kMemberType, kPass},
+        {Def::kBuiltinType, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
         {Def::kBuiltinType, Use::kValueExpression,
          R"(5:6 error: cannot use type 'vec4<f32>' as value
 7:8 note: are you missing '()' for type initializer?)"},
         {Def::kBuiltinType, Use::kVariableType, kPass},
 
         {Def::kFunction, Use::kCallExpr, kPass},
+        {Def::kFunction, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kFunction, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
         {Def::kFunction, Use::kCallStmt, kPass},
         {Def::kFunction, Use::kFunctionReturnType,
          R"(5:6 error: cannot use function 'FUNCTION' as type
@@ -181,30 +252,54 @@
         {Def::kFunction, Use::kMemberType,
          R"(5:6 error: cannot use function 'FUNCTION' as type
 1:2 note: 'FUNCTION' declared here)"},
+        {Def::kFunction, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
         {Def::kFunction, Use::kValueExpression, R"(7:8 error: missing '(' for function call)"},
         {Def::kFunction, Use::kVariableType,
          R"(5:6 error: cannot use function 'FUNCTION' as type
 1:2 note: 'FUNCTION' declared here)"},
 
-        {Def::kStruct, Use::kCallExpr, kPass},
+        {Def::kStruct, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kStruct, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
         {Def::kStruct, Use::kCallStmt, kPass},
         {Def::kStruct, Use::kFunctionReturnType, kPass},
         {Def::kStruct, Use::kMemberType, kPass},
+        {Def::kStruct, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
         {Def::kStruct, Use::kValueExpression,
          R"(5:6 error: cannot use type 'STRUCT' as value
 7:8 note: are you missing '()' for type initializer?
 1:2 note: 'STRUCT' declared here)"},
         {Def::kStruct, Use::kVariableType, kPass},
 
+        {Def::kTexelFormat, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kTexelFormat, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kTexelFormat, Use::kCallExpr,
+         R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"},
+        {Def::kTexelFormat, Use::kCallStmt,
+         R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"},
+        {Def::kTexelFormat, Use::kFunctionReturnType,
+         R"(5:6 error: cannot use texel format 'rgba8unorm' as type)"},
+        {Def::kTexelFormat, Use::kMemberType,
+         R"(5:6 error: cannot use texel format 'rgba8unorm' as type)"},
+        {Def::kTexelFormat, Use::kTexelFormat, kPass},
+        {Def::kTexelFormat, Use::kValueExpression,
+         R"(5:6 error: cannot use texel format 'rgba8unorm' as value)"},
+        {Def::kTexelFormat, Use::kVariableType,
+         R"(5:6 error: cannot use texel format 'rgba8unorm' as type)"},
+
+        {Def::kTypeAlias, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kTypeAlias, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
         {Def::kTypeAlias, Use::kCallExpr, kPass},
         {Def::kTypeAlias, Use::kCallStmt, kPass},
         {Def::kTypeAlias, Use::kFunctionReturnType, kPass},
         {Def::kTypeAlias, Use::kMemberType, kPass},
+        {Def::kTypeAlias, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
         {Def::kTypeAlias, Use::kValueExpression,
          R"(5:6 error: cannot use type 'i32' as value
 7:8 note: are you missing '()' for type initializer?)"},
         {Def::kTypeAlias, Use::kVariableType, kPass},
 
+        {Def::kVariable, Use::kAccess, R"(TODO(crbug.com/tint/1810))"},
+        {Def::kVariable, Use::kAddressSpace, R"(TODO(crbug.com/tint/1810))"},
         {Def::kVariable, Use::kCallStmt,
          R"(5:6 error: cannot use const 'VARIABLE' as call target
 1:2 note: 'VARIABLE' declared here)"},
@@ -217,6 +312,7 @@
         {Def::kVariable, Use::kMemberType,
          R"(5:6 error: cannot use const 'VARIABLE' as type
 1:2 note: 'VARIABLE' declared here)"},
+        {Def::kVariable, Use::kTexelFormat, R"(TODO(crbug.com/tint/1810))"},
         {Def::kVariable, Use::kValueExpression, kPass},
         {Def::kVariable, Use::kVariableType,
          R"(5:6 error: cannot use const 'VARIABLE' as type
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 47338a3..bb229d3 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -53,6 +53,7 @@
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/resolver/uniformity.h"
 #include "src/tint/sem/break_if_statement.h"
+#include "src/tint/sem/builtin_enum_expression.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/for_loop_statement.h"
 #include "src/tint/sem/function.h"
@@ -93,6 +94,10 @@
 #include "src/tint/utils/transform.h"
 #include "src/tint/utils/vector.h"
 
+TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::type::Access>);
+TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::type::AddressSpace>);
+TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::type::TexelFormat>);
+
 namespace tint::resolver {
 namespace {
 
@@ -2799,6 +2804,21 @@
         return nullptr;
     }
 
+    if (auto access = resolved->Access(); access != type::Access::kUndefined) {
+        return builder_->create<sem::BuiltinEnumExpression<type::Access>>(expr, current_statement_,
+                                                                          access);
+    }
+
+    if (auto addr = resolved->AddressSpace(); addr != type::AddressSpace::kUndefined) {
+        return builder_->create<sem::BuiltinEnumExpression<type::AddressSpace>>(
+            expr, current_statement_, addr);
+    }
+
+    if (auto fmt = resolved->TexelFormat(); fmt != type::TexelFormat::kUndefined) {
+        return builder_->create<sem::BuiltinEnumExpression<type::TexelFormat>>(
+            expr, current_statement_, fmt);
+    }
+
     TINT_UNREACHABLE(Resolver, diagnostics_)
         << "unhandled resolved identifier: " << resolved->String(builder_->Symbols(), diagnostics_);
     return nullptr;
diff --git a/src/tint/resolver/sem_helper.cc b/src/tint/resolver/sem_helper.cc
index 2c11659..bb769ab 100644
--- a/src/tint/resolver/sem_helper.cc
+++ b/src/tint/resolver/sem_helper.cc
@@ -14,6 +14,7 @@
 
 #include "src/tint/resolver/sem_helper.h"
 
+#include "src/tint/sem/builtin_enum_expression.h"
 #include "src/tint/sem/type_expression.h"
 #include "src/tint/sem/value_expression.h"
 
@@ -50,6 +51,18 @@
                 AddNote("'" + name + "' declared here", str->Source());
             }
         },
+        [&](const sem::BuiltinEnumExpression<type::Access>* access) {
+            AddError("cannot use access '" + utils::ToString(access->Value()) + "' as value",
+                     access->Declaration()->source);
+        },
+        [&](const sem::BuiltinEnumExpression<type::AddressSpace>* addr) {
+            AddError("cannot use address space '" + utils::ToString(addr->Value()) + "' as value",
+                     addr->Declaration()->source);
+        },
+        [&](const sem::BuiltinEnumExpression<type::TexelFormat>* fmt) {
+            AddError("cannot use texel format '" + utils::ToString(fmt->Value()) + "' as value",
+                     fmt->Declaration()->source);
+        },
         [&](Default) {
             TINT_ICE(Resolver, builder_->Diagnostics())
                 << "unhandled sem::Expression type: " << (expr ? expr->TypeInfo().name : "<null>");
diff --git a/src/tint/sem/builtin_enum_expression.cc b/src/tint/sem/builtin_enum_expression.cc
new file mode 100644
index 0000000..0e44220
--- /dev/null
+++ b/src/tint/sem/builtin_enum_expression.cc
@@ -0,0 +1,27 @@
+// Copyright 2023 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/sem/builtin_enum_expression.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpressionBase);
+
+namespace tint::sem {
+
+BuiltinEnumExpressionBase::BuiltinEnumExpressionBase(const ast::Expression* declaration,
+                                                     const Statement* statement)
+    : Base(declaration, statement) {}
+
+BuiltinEnumExpressionBase::~BuiltinEnumExpressionBase() = default;
+
+}  // namespace tint::sem
diff --git a/src/tint/sem/builtin_enum_expression.h b/src/tint/sem/builtin_enum_expression.h
new file mode 100644
index 0000000..3a403fb
--- /dev/null
+++ b/src/tint/sem/builtin_enum_expression.h
@@ -0,0 +1,67 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_SEM_BUILTIN_ENUM_EXPRESSION_H_
+#define SRC_TINT_SEM_BUILTIN_ENUM_EXPRESSION_H_
+
+#include "src/tint/sem/expression.h"
+
+// Forward declarations
+namespace tint::type {
+class Type;
+}  // namespace tint::type
+
+namespace tint::sem {
+
+/// Base class for BuiltinEnumExpression.
+/// Useful for Is() queries.
+class BuiltinEnumExpressionBase : public Castable<BuiltinEnumExpressionBase, Expression> {
+  public:
+    /// Constructor
+    /// @param declaration the AST node
+    /// @param statement the statement that owns this expression
+    BuiltinEnumExpressionBase(const ast::Expression* declaration, const Statement* statement);
+
+    /// Destructor
+    ~BuiltinEnumExpressionBase() override;
+};
+
+/// BuiltinEnumExpression holds the semantic information for expression nodes that resolve to a
+/// builtin enumerator value.
+template <typename ENUM>
+class BuiltinEnumExpression
+    : public Castable<BuiltinEnumExpression<ENUM>, BuiltinEnumExpressionBase> {
+  public:
+    /// Constructor
+    /// @param declaration the AST node
+    /// @param statement the statement that owns this expression
+    /// @param value the enumerator value
+    BuiltinEnumExpression(const ast::Expression* declaration,
+                          const Statement* statement,
+                          ENUM value)
+        : BuiltinEnumExpression<ENUM>::Base(declaration, statement), value_(value) {}
+
+    /// Destructor
+    ~BuiltinEnumExpression() override = default;
+
+    /// @return the enumerator value
+    ENUM Value() const { return value_; }
+
+  private:
+    const ENUM value_;
+};
+
+}  // namespace tint::sem
+
+#endif  // SRC_TINT_SEM_BUILTIN_ENUM_EXPRESSION_H_
diff --git a/src/tint/transform/renamer.cc b/src/tint/transform/renamer.cc
index 3d4f450..ec8dcee 100644
--- a/src/tint/transform/renamer.cc
+++ b/src/tint/transform/renamer.cc
@@ -18,6 +18,7 @@
 #include <utility>
 
 #include "src/tint/program_builder.h"
+#include "src/tint/sem/builtin_enum_expression.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/type_conversion.h"
@@ -1292,6 +1293,11 @@
                 preserved_identifiers.Add(diagnostic->control.rule_name);
             },
             [&](const ast::TypeName* ty) { preserve_if_builtin_type(ty->name); },
+            [&](const ast::IdentifierExpression* expr) {
+                if (src->Sem().Get<sem::BuiltinEnumExpressionBase>(expr)) {
+                    preserved_identifiers.Add(expr->identifier);
+                }
+            },
             [&](const ast::CallExpression* call) {
                 if (auto* ident = call->target.name) {
                     Switch(