tint/resolver: forbid spelling non-instantiable pointer types

Fixed: tint:1553
Change-Id: Ib77777a7142d302fe2d0d083de601f7b5042ad6a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111881
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/tint/resolver/address_space_validation_test.cc b/src/tint/resolver/address_space_validation_test.cc
index 90467b4..bc6c4a4 100644
--- a/src/tint/resolver/address_space_validation_test.cc
+++ b/src/tint/resolver/address_space_validation_test.cc
@@ -27,7 +27,7 @@
 
 using ResolverAddressSpaceValidationTest = ResolverTest;
 
-TEST_F(ResolverAddressSpaceValidationTest, GlobalVariableNoAddressSpace_Fail) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_NoAddressSpace_Fail) {
     // var g : f32;
     GlobalVar(Source{{12, 34}}, "g", ty.f32());
 
@@ -36,8 +36,16 @@
               "12:34 error: module-scope 'var' declaration must have a address space");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, GlobalVariableFunctionAddressSpace_Fail) {
-    // var<function> g : f32;
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_NoAddressSpace_Fail) {
+    // type g = ptr<f32>;
+    Alias("g", ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kUndefined));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: ptr missing address space");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_FunctionAddressSpace_Fail) {
+    // var<private> g : f32;
     GlobalVar(Source{{12, 34}}, "g", ty.f32(), ast::AddressSpace::kFunction);
 
     EXPECT_FALSE(r()->Resolve());
@@ -45,301 +53,611 @@
               "12:34 error: module-scope 'var' must not use address space 'function'");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, Private_RuntimeArray) {
-    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::AddressSpace::kPrivate);
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Private_RuntimeArray) {
+    // var<private> v : array<i32>;
+    GlobalVar(Source{{56, 78}}, "v", ty.array(Source{{12, 34}}, 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> address space
-12:34 note: while instantiating 'var' v)");
+56:78 note: while instantiating 'var' v)");
 }
 
-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::AddressSpace::kPrivate);
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Private_RuntimeArray) {
+    // type t : ptr<private, array<i32>>;
+    Alias("t", ty.pointer(Source{{56, 78}}, ty.array(Source{{12, 34}}, 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> address space
-note: while analyzing structure member S.m
-12:34 note: while instantiating 'var' v)");
+56:78 note: while instantiating ptr<private, array<i32>, read_write>)");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, Workgroup_RuntimeArray) {
-    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::AddressSpace::kWorkgroup);
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Private_RuntimeArrayInStruct) {
+    // struct S { m : array<i32> };
+    // var<private> v : S;
+    Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+    GlobalVar(Source{{56, 78}}, "v", ty.type_name("S"), ast::AddressSpace::kPrivate);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+56:78 note: while instantiating 'var' v)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Private_RuntimeArrayInStruct) {
+    // struct S { m : array<i32> };
+    // type t = ptr<private, S>;
+    Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+    Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kPrivate));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+note: while instantiating ptr<private, S, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Workgroup_RuntimeArray) {
+    // var<workgroup> v : array<i32>;
+    GlobalVar(Source{{56, 78}}, "v", ty.array(Source{{12, 34}}, 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> address space
-12:34 note: while instantiating 'var' v)");
+56:78 note: while instantiating 'var' v)");
 }
 
-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::AddressSpace::kWorkgroup);
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Workgroup_RuntimeArray) {
+    // type t = ptr<workgroup, array<i32>>;
+    Alias("t", ty.pointer(ty.array(Source{{12, 34}}, 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> address space
-note: while analyzing structure member S.m
-12:34 note: while instantiating 'var' v)");
+note: while instantiating ptr<workgroup, array<i32>, read_write>)");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferBool) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Workgroup_RuntimeArrayInStruct) {
+    // struct S { m : array<i32> };
+    // var<workgroup> v : S;
+    Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+    GlobalVar(Source{{56, 78}}, "v", ty.type_name("S"), ast::AddressSpace::kWorkgroup);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+56:78 note: while instantiating 'var' v)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Workgroup_RuntimeArrayInStruct) {
+    // struct S { m : array<i32> };
+    // type t = ptr<workgroup, S>;
+    Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+    Alias(Source{{56, 78}}, "t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kWorkgroup));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+note: while instantiating ptr<workgroup, S, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_Bool) {
     // var<storage> g : bool;
-    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kStorage, Binding(0_a),
-              Group(0_a));
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), 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 address space 'storage' as it is non-host-shareable
+        R"(12:34 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(ResolverAddressSpaceValidationTest, StorageBufferBoolAlias) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_Bool) {
+    // type t = ptr<storage, bool>;
+    Alias(Source{{56, 78}}, "t",
+          ty.pointer(ty.bool_(Source{{12, 34}}), ast::AddressSpace::kStorage));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+note: while instantiating ptr<storage, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_BoolAlias) {
     // type a = bool;
-    // var<storage, read> g : a;
-    auto* a = Alias("a", ty.bool_());
-    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 address space 'storage' as it is non-host-shareable
-56:78 note: while instantiating 'var' g)");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferPointer) {
-    // var<storage> g : ptr<private, f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+    // @binding(0) @group(0) var<storage, read> g : a;
+    Alias("a", ty.bool_());
+    GlobalVar(Source{{56, 78}}, "g", ty.type_name(Source{{12, 34}}, "a"),
               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 address space 'storage' as it is non-host-shareable
+        R"(12:34 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(ResolverAddressSpaceValidationTest, StorageBufferIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_BoolAlias) {
+    // type a = bool;
+    // type t = ptr<storage, a>;
+    Alias("a", ty.bool_());
+    Alias(Source{{56, 78}}, "t",
+          ty.pointer(ty.type_name(Source{{12, 34}}, "a"), ast::AddressSpace::kStorage));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+note: while instantiating ptr<storage, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_Pointer) {
+    // var<storage> g : ptr<private, f32>;
+    GlobalVar(Source{{56, 78}}, "g",
+              ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+              ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 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(ResolverAddressSpaceValidationTest, PointerAlias_Storage_Pointer) {
+    // type t = ptr<storage, ptr<private, f32>>;
+    Alias("t", ty.pointer(Source{{56, 78}},
+                          ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+                          ast::AddressSpace::kStorage));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 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 ptr<storage, ptr<private, f32, read_write>, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_IntScalar) {
     // var<storage> g : i32;
-    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kStorage, Binding(0_a),
-              Group(0_a));
+    GlobalVar("g", ty.i32(), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_IntScalar) {
+    // type t = ptr<storage, i32;
+    Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kStorage));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_F16) {
+    // enable f16;
     // var<storage> g : f16;
     Enable(ast::Extension::kF16);
 
-    GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
+    GlobalVar("g", ty.f16(), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16Alias) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_F16) {
+    // enable f16;
+    // type t = ptr<storage, f16>;
+    Enable(ast::Extension::kF16);
+
+    Alias("t", ty.pointer(ty.f16(), ast::AddressSpace::kStorage));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_F16Alias) {
+    // enable f16;
     // 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::AddressSpace::kStorage,
-              Binding(0_a), Group(0_a));
+    Alias("a", ty.f16());
+    GlobalVar("g", ty.type_name("a"), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_F16Alias) {
+    // enable f16;
+    // type a = f16;
+    // type t = ptr<storage, a>;
+    Enable(ast::Extension::kF16);
+
+    Alias("a", ty.f16());
+    Alias("t", ty.pointer(ty.type_name("a"), ast::AddressSpace::kStorage));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_VectorF32) {
     // var<storage> g : vec4<f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kStorage, Binding(0_a),
-              Group(0_a));
+    GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_VectorF32) {
+    // type t = ptr<storage, vec4<f32>>;
+    Alias("t", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kStorage));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_VectorF16) {
     // var<storage> g : vec4<f16>;
     Enable(ast::Extension::kF16);
-    GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kStorage,
-              Binding(0_a), Group(0_a));
+    GlobalVar("g", ty.vec(ty.f16(), 4u), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_VectorF16) {
+    // type t = ptr<storage, vec4<f16>>;
+    Enable(ast::Extension::kF16);
+    Alias("t", ty.pointer(ty.vec(ty.f16(), 4u), ast::AddressSpace::kStorage));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ArrayF32) {
+    // struct S{ a : f32 };
     // 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::AddressSpace::kStorage, ast::Access::kRead,
-              Binding(0_a), Group(0_a));
+    Structure("S", utils::Vector{Member("a", ty.f32())});
+    GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage,
+              ast::Access::kRead, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ArrayF32) {
+    // struct S{ a : f32 };
+    // type t = ptr<storage, array<S, 3u>>;
+    Structure("S", utils::Vector{Member("a", ty.f32())});
+    Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ArrayF16) {
+    // enable f16;
+    // 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())});
-    auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kStorage, ast::Access::kRead,
-              Binding(0_a), Group(0_a));
+    Structure("S", utils::Vector{Member("a", ty.f16())});
+    GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage,
+              ast::Access::kRead, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-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::AddressSpace::kStorage, ast::Access::kRead,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32Aliases) {
-    // 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::AddressSpace::kStorage, ast::Access::kRead,
-              Binding(0_a), Group(0_a));
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16) {
-    // struct S { x : f16 };
-    // var<storage, read> g : S;
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ArrayF16) {
+    // enable f16;
+    // struct S{ a : f16 };
+    // type t = ptr<storage, read, array<S, 3u>>;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
-    GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
+    Structure("S", utils::Vector{Member("a", ty.f16())});
+    Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage,
+                          ast::Access::kRead));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructI32) {
+    // struct S { x : i32 };
+    // var<storage, read> g : S;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16Aliases) {
-    // struct S { x : f16 };
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructI32) {
+    // struct S { x : i32 };
+    // type t = ptr<storage, read, S>;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructI32Aliases) {
+    // struct S { x : i32 };
     // type a1 = S;
     // var<storage, read> g : a1;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    Alias("a1", ty.type_name("S"));
+    Alias("a2", ty.type_name("a1"));
+    GlobalVar("g", ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructI32Aliases) {
+    // struct S { x : i32 };
+    // type a1 = S;
+    // type t = ptr<storage, read, a1>;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    Alias("a1", ty.type_name("S"));
+    Alias("a2", ty.type_name("a1"));
+    Alias("t", ty.pointer(ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructF16) {
+    // 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}}))});
-    auto* a1 = Alias("a1", ty.Of(s));
-    auto* a2 = Alias("a2", ty.Of(a1));
-    GlobalVar("g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, NotStorage_AccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructF16) {
+    // struct S { x : f16 };
+    // type t = ptr<storage, read, S>;
+    Enable(ast::Extension::kF16);
+
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructF16Aliases) {
+    // struct S { x : f16 };
+    // type a1 = S;
+    // var<storage, read> g : a1;
+    Enable(ast::Extension::kF16);
+
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    Alias("a1", ty.type_name("S"));
+    Alias("a2", ty.type_name("a1"));
+    GlobalVar("g", ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead,
+              Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructF16Aliases) {
+    // struct S { x : f16 };
+    // type a1 = S;
+    // type t = ptr<storage, read, a1>;
+    Enable(ast::Extension::kF16);
+
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    Alias("a1", ty.type_name("S"));
+    Alias("a2", ty.type_name("a1"));
+    Alias("g", ty.pointer(ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_NotStorage_AccessMode) {
     // var<private, read> g : a;
-    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead);
+    GlobalVar(Source{{12, 34}}, "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> address space may declare an access mode)");
+        R"(12:34 error: only variables in <storage> address space may declare an access mode)");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, Storage_ReadAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_NotStorage_AccessMode) {
+    // type t = ptr<private, read, a>;
+    Alias("t",
+          ty.pointer(Source{{12, 34}}, ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: only pointers in <storage> address space may declare an access mode)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ReadAccessMode) {
     // @group(0) @binding(0) var<storage, read> a : i32;
-    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead,
-              Group(0_a), Binding(0_a));
+    GlobalVar("a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead, Group(0_a),
+              Binding(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, Storage_ReadWriteAccessMode) {
-    // @group(0) @binding(0) var<storage, read_write> a : i32;
-    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
-              Group(0_a), Binding(0_a));
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ReadAccessMode) {
+    // type t = ptr<storage, read, i32>;
+    Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, Storage_WriteAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ReadWriteAccessMode) {
     // @group(0) @binding(0) var<storage, read_write> a : i32;
-    GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite,
+    GlobalVar("a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
+              Binding(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ReadWriteAccessMode) {
+    // type t = ptr<storage, read_write, i32>;
+    Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_WriteAccessMode) {
+    // @group(0) @binding(0) var<storage, read_write> a : i32;
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite,
               Group(0_a), Binding(0_a));
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              R"(56:78 error: access mode 'write' is not valid for the 'storage' address space)");
+              R"(12:34 error: access mode 'write' is not valid for the 'storage' address space)");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBuffer_Struct_Runtime) {
-    // struct S { m:  array<f32>; };
-    // @group(0) @binding(0) var<uniform, > svar : S;
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_WriteAccessMode) {
+    // type t = ptr<storage, read_write, i32>;
+    Alias("t",
+          ty.pointer(Source{{12, 34}}, ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite));
 
-    auto* s = Structure(Source{{12, 34}}, "S", utils::Vector{Member("m", ty.array<i32>())});
+    ASSERT_FALSE(r()->Resolve());
 
-    GlobalVar(Source{{56, 78}}, "svar", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    EXPECT_EQ(r()->error(),
+              R"(12:34 error: access mode 'write' is not valid for the 'storage' address space)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBuffer_Struct_Runtime) {
+    // struct S { m: array<f32>; };
+    // @group(0) @binding(0) var<uniform> svar : S;
+
+    Structure("S",
+              utils::Vector{Member(Source{{56, 78}}, "m", ty.array(Source{{12, 34}}, ty.i32()))});
+
+    GlobalVar(Source{{90, 12}}, "svar", ty.type_name("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> address space
-note: while analyzing structure member S.m
-56:78 note: while instantiating 'var' svar)");
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
+56:78 note: while analyzing structure member S.m
+90:12 note: while instantiating 'var' svar)");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferBool) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBuffer_Struct_Runtime) {
+    // struct S { m: array<f32>; };
+    // type t = ptr<uniform, S>;
+
+    Structure("S",
+              utils::Vector{Member(Source{{56, 78}}, "m", ty.array(Source{{12, 34}}, ty.i32()))});
+
+    Alias("t", ty.pointer(Source{{90, 12}}, ty.type_name("S"), ast::AddressSpace::kUniform));
+
+    ASSERT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 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.
+note: see layout of struct:
+/*           align(4) size(4) */ struct S {
+/* offset(0) align(4) size(4) */   m : array<i32>;
+/*                            */ };
+90:12 note: 'S' used in address space 'uniform' here)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferBool) {
     // var<uniform> g : bool;
-    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), 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 address space 'uniform' as it is non-host-shareable
+        R"(12:34 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(ResolverAddressSpaceValidationTest, UniformBufferBoolAlias) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferBool) {
+    // type t = ptr<uniform, bool>;
+    Alias("t",
+          ty.pointer(Source{{56, 78}}, ty.bool_(Source{{12, 34}}), ast::AddressSpace::kUniform));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
+56:78 note: while instantiating ptr<uniform, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferBoolAlias) {
     // type a = bool;
     // var<uniform> g : a;
-    auto* a = Alias("a", ty.bool_());
-    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 address space 'uniform' as it is non-host-shareable
-56:78 note: while instantiating 'var' g)");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferPointer) {
-    // var<uniform> g : ptr<private, f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+    Alias("a", ty.bool_());
+    GlobalVar(Source{{56, 78}}, "g", ty.type_name(Source{{12, 34}}, "a"),
               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 address space 'uniform' as it is non-host-shareable
+        R"(12:34 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(ResolverAddressSpaceValidationTest, UniformBufferIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferBoolAlias) {
+    // type a = bool;
+    // type t = ptr<uniform, a>;
+    Alias("a", ty.bool_());
+    Alias("t", ty.pointer(Source{{56, 78}}, ty.type_name(Source{{12, 34}}, "a"),
+                          ast::AddressSpace::kUniform));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
+56:78 note: while instantiating ptr<uniform, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformPointer) {
+    // var<uniform> g : ptr<private, f32>;
+    GlobalVar(Source{{56, 78}}, "g",
+              ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+              ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 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(ResolverAddressSpaceValidationTest, PointerAlias_UniformPointer) {
+    // type t = ptr<uniform, ptr<private, f32>>;
+    Alias("t", ty.pointer(Source{{56, 78}},
+                          ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+                          ast::AddressSpace::kUniform));
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 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 ptr<uniform, ptr<private, f32, read_write>, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferIntScalar) {
     // var<uniform> g : i32;
     GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
@@ -347,124 +665,241 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferIntScalar) {
+    // type t = ptr<uniform, i32>;
+    Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferF16) {
+    // enable f16;
     // var<uniform> g : f16;
     Enable(ast::Extension::kF16);
 
-    GlobalVar(Source{{56, 78}}, "g", ty.f16(), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    GlobalVar("g", ty.f16(), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferF16) {
+    // enable f16;
+    // type t = ptr<uniform, f16>;
+    Enable(ast::Extension::kF16);
+
+    Alias("t", ty.pointer(ty.f16(), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferVectorF32) {
     // var<uniform> g : vec4<f32>;
-    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferVectorF32) {
+    // type t = ptr<uniform, vec4<f32>>;
+    Alias("t", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferVectorF16) {
+    // enable f16;
     // var<uniform> g : vec4<f16>;
     Enable(ast::Extension::kF16);
 
-    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f16>(), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferVectorF16) {
+    // enable f16;
+    // type t = ptr<uniform, vec4<f16>>;
+    Enable(ast::Extension::kF16);
+
+    Alias("t", ty.pointer(ty.vec4<f16>(), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_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::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+    Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16_a)})});
+    GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform, Binding(0_a),
+              Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferArrayF32) {
+    // struct S {
+    //   @size(16) f : f32;
+    // }
+    // type t = ptr<uniform, array<S, 3u>>;
+    Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16_a)})});
+    Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferArrayF16) {
+    // enable f16;
     // struct S {
     //   @size(16) f : f16;
     // }
     // var<uniform> g : array<S, 3u>;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
-    auto* a = ty.array(ty.Of(s), 3_u);
-    GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-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::AddressSpace::kUniform, Binding(0_a),
+    Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
+    GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform, Binding(0_a),
               Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructI32Aliases) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferArrayF16) {
+    // enable f16;
+    // struct S {
+    //   @size(16) f : f16;
+    // }
+    // type t = ptr<uniform, array<S, 3u>>;
+    Enable(ast::Extension::kF16);
+
+    Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
+    Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructI32) {
+    // struct S { x : i32 };
+    // var<uniform> g : S;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructI32) {
+    // struct S { x : i32 };
+    // type t = ptr<uniform, S>;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_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::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    Alias("a1", ty.type_name("S"));
+    GlobalVar("g", ty.type_name("a1"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructI32Aliases) {
+    // struct S { x : i32 };
+    // type a1 = S;
+    // type t = ptr<uniform, a1>;
+    Structure("S", utils::Vector{Member("x", ty.i32())});
+    Alias("a1", ty.type_name("S"));
+    Alias("t", ty.pointer(ty.type_name("a1"), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructF16) {
+    // enable f16;
     // struct S { x : f16 };
-    // var<uniform> g :  S;
+    // var<uniform> g : S;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.f16())});
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16Aliases) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructF16) {
+    // enable f16;
+    // struct S { x : f16 };
+    // type t = ptr<uniform, S>;
+    Enable(ast::Extension::kF16);
+
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructF16Aliases) {
+    // enable f16;
     // struct S { x : f16 };
     // type a1 = S;
     // var<uniform> g : a1;
     Enable(ast::Extension::kF16);
 
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.f16())});
-    auto* a1 = Alias("a1", ty.Of(s));
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a),
-              Group(0_a));
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    Alias("a1", ty.type_name("S"));
+    GlobalVar("g", ty.type_name("a1"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantBool) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructF16Aliases) {
+    // enable f16;
+    // struct S { x : f16 };
+    // type a1 = S;
+    // type t = ptr<uniform, a1>;
+    Enable(ast::Extension::kF16);
+
+    Structure("S", utils::Vector{Member("x", ty.f16())});
+    Alias("a1", ty.type_name("S"));
+    Alias("t", ty.pointer(ty.type_name("a1"), ast::AddressSpace::kUniform));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantBool) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : bool;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kPushConstant);
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), ast::AddressSpace::kPushConstant);
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
         r()->error(),
-        R"(56:78 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable
+        R"(12:34 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(ResolverAddressSpaceValidationTest, PushConstantF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantBool) {
     // enable chromium_experimental_push_constant;
+    // type t = ptr<push_constant, bool>;
+    Enable(ast::Extension::kChromiumExperimentalPushConstant);
+    Alias(Source{{56, 78}}, "t",
+          ty.pointer(ty.bool_(Source{{12, 34}}), ast::AddressSpace::kPushConstant));
+
+    ASSERT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable
+note: while instantiating ptr<push_constant, bool, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantF16) {
     // enable f16;
+    // enable chromium_experimental_push_constant;
     // var<push_constant> g : f16;
     Enable(ast::Extension::kF16);
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
@@ -475,21 +910,50 @@
               "error: using f16 types in 'push_constant' address space is not implemented yet");
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantPointer) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantF16) {
+    // enable f16;
+    // enable chromium_experimental_push_constant;
+    // type t = ptr<push_constant, f16>;
+    Enable(ast::Extension::kF16);
+    Enable(ast::Extension::kChromiumExperimentalPushConstant);
+    Alias("t", ty.pointer(ty.f16(Source{{56, 78}}), ast::AddressSpace::kPushConstant));
+
+    ASSERT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "error: using f16 types in 'push_constant' address space is not implemented yet");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_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::AddressSpace::kPrivate),
+    GlobalVar(Source{{56, 78}}, "g",
+              ty.pointer(Source{{12, 34}}, 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 address space 'push_constant' as it is non-host-shareable
+        R"(12:34 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(ResolverAddressSpaceValidationTest, PushConstantIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantPointer) {
+    // enable chromium_experimental_push_constant;
+    // type t = ptr<push_constant, ptr<private, f32>>;
+    Enable(ast::Extension::kChromiumExperimentalPushConstant);
+    Alias(Source{{56, 78}}, "t",
+          ty.pointer(ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+                     ast::AddressSpace::kPushConstant));
+
+    ASSERT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'push_constant' as it is non-host-shareable
+note: while instantiating ptr<push_constant, ptr<private, f32, read_write>, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantIntScalar) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : i32;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
@@ -498,7 +962,16 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantIntScalar) {
+    // enable chromium_experimental_push_constant;
+    // type t = ptr<push_constant, i32>;
+    Enable(ast::Extension::kChromiumExperimentalPushConstant);
+    Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kPushConstant));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantVectorF32) {
     // enable chromium_experimental_push_constant;
     // var<push_constant> g : vec4<f32>;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
@@ -507,29 +980,35 @@
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantVectorF32) {
     // enable chromium_experimental_push_constant;
-    // struct S { a : f32}
-    // var<push_constant> g : array<S, 3u>;
+    // var<push_constant> g : vec4<f32>;
     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::AddressSpace::kPushConstant);
+    Alias("t", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kPushConstant));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantWithInitializer) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantArrayF32) {
     // enable chromium_experimental_push_constant;
-    // var<push_constant> a : u32 = 0u;
+    // struct S { a : f32}
+    // var<push_constant> g : array<S, 3u>;
     Enable(ast::Extension::kChromiumExperimentalPushConstant);
-    GlobalVar(Source{{1u, 2u}}, "a", ty.u32(), ast::AddressSpace::kPushConstant,
-              Expr(Source{{3u, 4u}}, u32(0)));
+    Structure("S", utils::Vector{Member("a", ty.f32())});
+    GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kPushConstant);
 
-    ASSERT_FALSE(r()->Resolve());
-    EXPECT_EQ(
-        r()->error(),
-        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')");
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantArrayF32) {
+    // enable chromium_experimental_push_constant;
+    // struct S { a : f32}
+    // type t = ptr<push_constant, array<S, 3u>>;
+    Enable(ast::Extension::kChromiumExperimentalPushConstant);
+    Structure("S", utils::Vector{Member("a", ty.f32())});
+    Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kPushConstant));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 }  // namespace
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index 97519cd..325595c 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -26,49 +26,52 @@
 using ResolverHostShareableValidationTest = ResolverTest;
 
 TEST_F(ResolverHostShareableValidationTest, BoolMember) {
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.bool_())});
+    auto* s =
+        Structure("S", utils::Vector{Member(Source{{56, 78}}, "x", ty.bool_(Source{{12, 34}}))});
 
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{90, 12}}, "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 address space 'storage' as it is non-host-shareable
-12:34 note: while analyzing structure member S.x
-56:78 note: while instantiating 'var' g)");
+        R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while analyzing structure member S.x
+90:12 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
+    auto* s = Structure(
+        "S", utils::Vector{Member(Source{{56, 78}}, "x", ty.vec3<bool>(Source{{12, 34}}))});
 
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{90, 12}}, "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 address space 'storage' as it is non-host-shareable
-12:34 note: while analyzing structure member S.x
-56:78 note: while instantiating 'var' g)");
+        R"(12:34 error: Type 'vec3<bool>' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while analyzing structure member S.x
+90:12 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, Aliases) {
-    auto* a1 = Alias("a1", ty.bool_());
-    auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.Of(a1))});
+    Alias("a1", ty.bool_());
+    auto* s = Structure(
+        "S", utils::Vector{Member(Source{{56, 78}}, "x", ty.type_name(Source{{12, 34}}, "a1"))});
     auto* a2 = Alias("a2", ty.Of(s));
-    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
+    GlobalVar(Source{{90, 12}}, "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 address space 'storage' as it is non-host-shareable
-12:34 note: while analyzing structure member S.x
-56:78 note: while instantiating 'var' g)");
+        R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while analyzing structure member S.x
+90:12 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
@@ -85,7 +88,7 @@
 
     EXPECT_EQ(
         r()->error(),
-        R"(9:10 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+        R"(error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
 1:2 note: while analyzing structure member I1.x
 3:4 note: while analyzing structure member I2.y
 5:6 note: while analyzing structure member I3.z
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index ccabe39..cf55a1d 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -262,7 +262,18 @@
                 if (access == ast::Access::kUndefined) {
                     access = DefaultAccessForAddressSpace(t->address_space);
                 }
-                return builder_->create<sem::Pointer>(el, t->address_space, access);
+                auto ptr = builder_->create<sem::Pointer>(el, t->address_space, access);
+                if (!ptr) {
+                    return nullptr;
+                }
+                if (!validator_.Pointer(t, ptr)) {
+                    return nullptr;
+                }
+                if (!ApplyAddressSpaceUsageToType(t->address_space, el, t->type->source)) {
+                    AddNote("while instantiating " + builder_->FriendlyName(ptr), t->source);
+                    return nullptr;
+                }
+                return ptr;
             }
             return nullptr;
         },
@@ -625,7 +636,8 @@
 
     auto* var_ty = builder_->create<sem::Reference>(storage_ty, address_space, access);
 
-    if (!ApplyAddressSpaceUsageToType(address_space, var_ty, var->source)) {
+    if (!ApplyAddressSpaceUsageToType(address_space, var_ty,
+                                      var->type ? var->type->source : var->source)) {
         AddNote("while instantiating 'var' " + builder_->Symbols().NameFor(var->symbol),
                 var->source);
         return nullptr;
@@ -727,7 +739,7 @@
         return nullptr;
     }
 
-    if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, ty, param->source)) {
+    if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, ty, param->type->source)) {
         add_note();
         return nullptr;
     }
@@ -3562,12 +3574,14 @@
         str->AddUsage(address_space);
 
         for (auto* member : str->Members()) {
-            if (!ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(member->Type()),
-                                              usage)) {
+            auto decl = member->Declaration();
+            if (decl &&
+                !ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(member->Type()),
+                                              decl->type->source)) {
                 std::stringstream err;
                 err << "while analyzing structure member " << sem_.TypeNameOf(str) << "."
-                    << builder_->Symbols().NameFor(member->Declaration()->symbol);
-                AddNote(err.str(), member->Declaration()->source);
+                    << builder_->Symbols().NameFor(decl->symbol);
+                AddNote(err.str(), decl->source);
                 return false;
             }
         }
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index d44c9d8..68a7017 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -608,7 +608,7 @@
     /// @return a new AST alias type
     static inline const ast::Type* AST(ProgramBuilder& b) {
         return b.create<ast::Pointer>(DataType<T>::AST(b), ast::AddressSpace::kPrivate,
-                                      ast::Access::kReadWrite);
+                                      ast::Access::kUndefined);
     }
     /// @param b the ProgramBuilder
     /// @return the semantic aliased type
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 59367d0..fe1aa05 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -321,12 +321,11 @@
 
 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)),
+    GlobalVar(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.f32(), Expr(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)");
+    EXPECT_EQ(r()->error(), R"(12:34 error: array count (65536) must be less than 65536
+56:78 note: while instantiating 'var' a)");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_StorageBufferLargeArray) {
@@ -569,7 +568,7 @@
     /// @vertex
     // fn func() { var a : array<i32>; }
 
-    auto* var = Var(Source{{12, 34}}, "a", ty.array<i32>());
+    auto* var = Var(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.i32()));
 
     Func("func", utils::Empty, ty.void_(),
          utils::Vector{
@@ -582,7 +581,7 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating 'var' a)");
+56:78 note: while instantiating 'var' a)");
 }
 
 TEST_F(ResolverTypeValidationTest, Struct_Member_VectorNoType) {
@@ -731,23 +730,24 @@
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayAsGlobalVariable) {
-    GlobalVar(Source{{56, 78}}, "g", ty.array<i32>(), ast::AddressSpace::kPrivate);
+    GlobalVar(Source{{56, 78}}, "g", ty.array(Source{{12, 34}}, ty.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> address space
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayAsLocalVariable) {
-    auto* v = Var(Source{{56, 78}}, "g", ty.array<i32>());
+    auto* v = Var(Source{{56, 78}}, "g", ty.array(Source{{12, 34}}, ty.i32()));
     WrapInFunction(v);
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
-              R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
 56:78 note: while instantiating 'var' g)");
 }
 
@@ -755,7 +755,7 @@
     // fn func(a : array<u32>) {}
     // @vertex fn main() {}
 
-    auto* param = Param(Source{{12, 34}}, "a", ty.array<i32>());
+    auto* param = Param(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.i32()));
 
     Func("func", utils::Vector{param}, ty.void_(),
          utils::Vector{
@@ -773,14 +773,14 @@
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating parameter a)");
+56:78 note: while instantiating parameter a)");
 }
 
-TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsParameter_Fail) {
+TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsPointerParameter_Fail) {
     // fn func(a : ptr<workgroup, array<u32>>) {}
 
-    auto* param =
-        Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::AddressSpace::kWorkgroup));
+    auto* param = Param("a", ty.pointer(Source{{56, 78}}, ty.array(Source{{12, 34}}, ty.i32()),
+                                        ast::AddressSpace::kWorkgroup));
 
     Func("func", utils::Vector{param}, ty.void_(),
          utils::Vector{
@@ -790,7 +790,23 @@
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating parameter a)");
+56:78 note: while instantiating ptr<workgroup, array<i32>, read_write>)");
+}
+
+TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsParameter_Fail) {
+    // fn func(a : ptr<workgroup, array<u32>>) {}
+
+    auto* param = Param(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.i32()));
+
+    Func("func", utils::Vector{param}, ty.void_(),
+         utils::Vector{
+             Return(),
+         });
+
+    EXPECT_FALSE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(),
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
+56:78 note: while instantiating parameter a)");
 }
 
 TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsNotLast_Fail) {
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 6a64d5a..70e1de1 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -269,6 +269,28 @@
     return true;
 }
 
+bool Validator::Pointer(const ast::Pointer* a, const sem::Pointer* s) const {
+    if (s->AddressSpace() == ast::AddressSpace::kUndefined) {
+        AddError("ptr missing address space", a->source);
+        return false;
+    }
+
+    if (a->access != ast::Access::kUndefined) {
+        // https://www.w3.org/TR/WGSL/#access-mode-defaults
+        // When writing a variable declaration or a pointer type in WGSL source:
+        // * For the storage address space, the access mode is optional, and defaults to read.
+        // * For other address spaces, the access mode must not be written.
+        if (a->address_space != ast::AddressSpace::kStorage) {
+            AddError("only pointers in <storage> address space may declare an access mode",
+                     a->source);
+            return false;
+        }
+    }
+
+    return CheckTypeAccessAddressSpace(s->StoreType(), s->Access(), s->AddressSpace(), utils::Empty,
+                                       a->source);
+}
+
 bool Validator::StorageTexture(const ast::StorageTexture* t) const {
     switch (t->access) {
         case ast::Access::kWrite:
@@ -355,11 +377,10 @@
             default:
                 // https://gpuweb.github.io/gpuweb/wgsl/#var-and-let
                 // Optionally has an initializer expression, if the variable is in the private or
-                // function address spacees.
+                // function address spaces.
                 AddError("var of address space '" + utils::ToString(address_space) +
-                             "' cannot have an initializer. var initializers are only "
-                             "supported for the address spacees "
-                             "'private' and 'function'",
+                             "' cannot have an initializer. var initializers are only supported "
+                             "for the address spaces 'private' and 'function'",
                          v->source);
                 return false;
         }
@@ -672,9 +693,10 @@
     }
 
     if (var->declared_access != ast::Access::kUndefined) {
-        // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
-        // The access mode always has a default, and except for variables in the storage address
-        // space, must not be written.
+        // https://www.w3.org/TR/WGSL/#access-mode-defaults
+        // When writing a variable declaration or a pointer type in WGSL source:
+        // * For the storage address space, the access mode is optional, and defaults to read.
+        // * For other address spaces, the access mode must not be written.
         if (var->declared_address_space != ast::AddressSpace::kStorage) {
             AddError("only variables in <storage> address space may declare an access mode",
                      var->source);
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 5c0188f..0f183bb 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -169,6 +169,12 @@
     /// @returns true on success, false otherwise.
     bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const;
 
+    /// Validates a pointer type
+    /// @param a the pointer ast node
+    /// @param s the pointer sem node
+    /// @returns true on success, false otherwise.
+    bool Pointer(const ast::Pointer* a, const sem::Pointer* s) const;
+
     /// Validates an assignment
     /// @param a the assignment statement
     /// @param rhs_ty the type of the right hand side
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 964c8d9..cf35e4c 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -321,16 +321,16 @@
 
 TEST_F(ResolverVariableValidationTest, NonConstructibleType_RuntimeArray) {
     auto* s = Structure("S", utils::Vector{
-                                 Member(Source{{56, 78}}, "m", ty.array(ty.i32())),
+                                 Member(Source{{12, 34}}, "m", ty.array<i32>()),
                              });
-    auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
+    auto* v = Var(Source{{56, 78}}, "v", ty.Of(s));
     WrapInFunction(v);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-56:78 note: while analyzing structure member S.m
-12:34 note: while instantiating 'var' v)");
+              R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+56:78 note: while instantiating 'var' v)");
 }
 
 TEST_F(ResolverVariableValidationTest, NonConstructibleType_Struct_WithAtomic) {
@@ -365,7 +365,7 @@
     EXPECT_EQ(r()->error(),
               "12:34 error: var of address space 'workgroup' cannot have "
               "an initializer. var initializers are only supported for the "
-              "address spacees 'private' and 'function'");
+              "address spaces 'private' and 'function'");
 }
 
 TEST_F(ResolverVariableValidationTest, VectorConstNoType) {
@@ -486,5 +486,18 @@
 56:78 note: consider changing 'const' to 'let')");
 }
 
+TEST_F(ResolverVariableValidationTest, GlobalVariable_PushConstantWithInitializer) {
+    // enable chromium_experimental_push_constant;
+    // var<push_constant> a : u32 = 0u;
+    Enable(ast::Extension::kChromiumExperimentalPushConstant);
+    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 address space 'push_constant' cannot have an initializer. var initializers are only supported for the address spaces 'private' and 'function')");
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 3be550c..944797a 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -466,6 +466,9 @@
                     const sem::VariableUser* var_user) {
         auto address_space = var_user->Variable()->AddressSpace();
         auto access = var_user->Variable()->Access();
+        if (address_space != ast::AddressSpace::kStorage) {
+            access = ast::Access::kUndefined;
+        }
         return utils::GetOrCreate(
             load_funcs, LoadStoreKey{address_space, access, buf_ty, el_ty}, [&] {
                 utils::Vector params{
@@ -562,6 +565,9 @@
                      const sem::VariableUser* var_user) {
         auto address_space = var_user->Variable()->AddressSpace();
         auto access = var_user->Variable()->Access();
+        if (address_space != ast::AddressSpace::kStorage) {
+            access = ast::Access::kUndefined;
+        }
         return utils::GetOrCreate(
             store_funcs, LoadStoreKey{address_space, access, buf_ty, el_ty}, [&] {
                 utils::Vector params{
@@ -670,7 +676,11 @@
                       const sem::Builtin* intrinsic,
                       const sem::VariableUser* var_user) {
         auto op = intrinsic->Type();
+        auto address_space = var_user->Variable()->AddressSpace();
         auto access = var_user->Variable()->Access();
+        if (address_space != ast::AddressSpace::kStorage) {
+            access = ast::Access::kUndefined;
+        }
         return utils::GetOrCreate(atomic_funcs, AtomicKey{access, buf_ty, el_ty, op}, [&] {
             // The first parameter to all WGSL atomics is the expression to the
             // atomic. This is replaced with two parameters: the buffer and offset.
diff --git a/src/tint/transform/decompose_memory_access_test.cc b/src/tint/transform/decompose_memory_access_test.cc
index ac798e0..4c2cd11 100644
--- a/src/tint/transform/decompose_memory_access_test.cc
+++ b/src/tint/transform/decompose_memory_access_test.cc
@@ -804,126 +804,126 @@
 @group(0) @binding(0) var<uniform> ub : UB;
 
 @internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f32
 
 @internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> i32
 
 @internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> u32
 
 @internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f16
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f16
 
 @internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f32>
 
 @internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<i32>
 
 @internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<u32>
 
 @internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f16>
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f32>
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f32>
 
 @internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<i32>
 
 @internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<u32>
 
 @internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f16>
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f32>
 
 @internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<i32>
 
 @internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<u32>
 
 @internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f16>
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f16>
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f32> {
   return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f32> {
   return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f32> {
   return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f32> {
   return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f32> {
   return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f32> {
   return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f16> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f16> {
   return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
 }
 
-fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f16> {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f16> {
   return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f16> {
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f16> {
   return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f16> {
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f16> {
   return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f16> {
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f16> {
   return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f16> {
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f16> {
   return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f16> {
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f16> {
   return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
 }
 
-fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f16> {
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f16> {
   return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f16> {
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f16> {
   return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
   for(var i = 0u; (i < 2u); i = (i + 1u)) {
     arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
@@ -931,7 +931,7 @@
   return arr;
 }
 
-fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<mat4x2<f16>, 2u> {
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<mat4x2<f16>, 2u> {
   var arr_1 : array<mat4x2<f16>, 2u>;
   for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
     arr_1[i_1] = tint_symbol_31(buffer, (offset + (i_1 * 16u)));
@@ -1075,126 +1075,126 @@
 enable f16;
 
 @internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f32
 
 @internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> i32
 
 @internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> u32
 
 @internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f16
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f16
 
 @internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f32>
 
 @internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<i32>
 
 @internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<u32>
 
 @internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f16>
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f16>
 
 @internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f32>
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f32>
 
 @internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<i32>
 
 @internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<u32>
 
 @internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f16>
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f16>
 
 @internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f32>
 
 @internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<i32>
 
 @internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<u32>
 
 @internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f16>
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f16>
 
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f32> {
   return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f32> {
   return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f32> {
   return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f32> {
   return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f32> {
   return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f32> {
   return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
 }
 
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f32> {
   return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f32> {
   return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f32> {
   return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
 }
 
-fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f16> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f16> {
   return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
 }
 
-fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f16> {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f16> {
   return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f16> {
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f16> {
   return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f16> {
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f16> {
   return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
 }
 
-fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f16> {
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f16> {
   return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f16> {
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f16> {
   return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
 }
 
-fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f16> {
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f16> {
   return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
 }
 
-fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f16> {
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f16> {
   return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f16> {
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f16> {
   return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
 }
 
-fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<vec3<f32>, 2u> {
   var arr : array<vec3<f32>, 2u>;
   for(var i = 0u; (i < 2u); i = (i + 1u)) {
     arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
@@ -1202,7 +1202,7 @@
   return arr;
 }
 
-fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<mat4x2<f16>, 2u> {
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<mat4x2<f16>, 2u> {
   var arr_1 : array<mat4x2<f16>, 2u>;
   for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
     arr_1[i_1] = tint_symbol_31(buffer, (offset + (i_1 * 16u)));
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 8a69bd3..9c9c42f 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
@@ -139,7 +139,7 @@
             }
             case ast::AddressSpace::kStorage:
             case ast::AddressSpace::kUniform: {
-                // Variables into the Storage and Uniform address spacees are redeclared as entry
+                // Variables into the Storage and Uniform address spaces 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));
@@ -192,7 +192,7 @@
                 [[fallthrough]];
             }
             case ast::AddressSpace::kPrivate: {
-                // Variables in the Private and Workgroup address spacees are redeclared at function
+                // Variables in the Private and Workgroup address spaces are redeclared at function
                 // scope. Disable address space validation on this variable.
                 auto* disable_validation =
                     ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace);
@@ -497,7 +497,7 @@
             }
         }
 
-        // Now remove all module-scope variables with these address spacees.
+        // Now remove all module-scope variables with these address spaces.
         for (auto* var_ast : ctx.src->AST().GlobalVariables()) {
             auto* var_sem = ctx.src->Sem().Get(var_ast);
             if (var_sem->AddressSpace() != ast::AddressSpace::kNone) {