[ir] Add validation for core types only

Add validation to the IR validator to check that all types are core
types.

Change-Id: I173b59db2554c54ac4f1ae63b83af8dc1cd1519a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/247714
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/transform/builtin_scalarize.h b/src/tint/lang/core/ir/transform/builtin_scalarize.h
index cfc9c7a..05673f9 100644
--- a/src/tint/lang/core/ir/transform/builtin_scalarize.h
+++ b/src/tint/lang/core/ir/transform/builtin_scalarize.h
@@ -53,6 +53,7 @@
     core::ir::Capability::kAllowWorkspacePointerInputToEntryPoint,
     core::ir::Capability::kAllowModuleScopeLets,
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// The scalarizer configuration options
diff --git a/src/tint/lang/core/ir/transform/dead_code_elimination.h b/src/tint/lang/core/ir/transform/dead_code_elimination.h
index fdf7fc9..c37de06 100644
--- a/src/tint/lang/core/ir/transform/dead_code_elimination.h
+++ b/src/tint/lang/core/ir/transform/dead_code_elimination.h
@@ -44,6 +44,7 @@
     core::ir::Capability::kAllowVectorElementPointer,
     core::ir::Capability::kAllowPhonyInstructions,
     core::ir::Capability::kAllowUnannotatedModuleIOVariables,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// DeadCodeElimination is a transform that removes dead code from the given IR module.
diff --git a/src/tint/lang/core/ir/transform/remove_continue_in_switch.cc b/src/tint/lang/core/ir/transform/remove_continue_in_switch.cc
index 5c02fb0..9169d7d 100644
--- a/src/tint/lang/core/ir/transform/remove_continue_in_switch.cc
+++ b/src/tint/lang/core/ir/transform/remove_continue_in_switch.cc
@@ -119,6 +119,7 @@
                                               core::ir::Capability::kAllowHandleVarsWithoutBindings,
                                               core::ir::Capability::kAllowClipDistancesOnF32,
                                               core::ir::Capability::kAllowDuplicateBindings,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result;
diff --git a/src/tint/lang/core/ir/transform/remove_terminator_args.h b/src/tint/lang/core/ir/transform/remove_terminator_args.h
index 24832fb..10bff59 100644
--- a/src/tint/lang/core/ir/transform/remove_terminator_args.h
+++ b/src/tint/lang/core/ir/transform/remove_terminator_args.h
@@ -52,6 +52,7 @@
     core::ir::Capability::kAllowModuleScopeLets,
     core::ir::Capability::kAllowWorkspacePointerInputToEntryPoint,
     core::ir::Capability::kAllowDuplicateBindings,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// RemoveTerminatorArgs is a transform that removes all arguments from terminator instructions and
diff --git a/src/tint/lang/core/ir/transform/rename_conflicts.h b/src/tint/lang/core/ir/transform/rename_conflicts.h
index dbd83c7..37d4686 100644
--- a/src/tint/lang/core/ir/transform/rename_conflicts.h
+++ b/src/tint/lang/core/ir/transform/rename_conflicts.h
@@ -52,6 +52,7 @@
     core::ir::Capability::kAllowModuleScopeLets,
     core::ir::Capability::kAllowWorkspacePointerInputToEntryPoint,
     core::ir::Capability::kAllowDuplicateBindings,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// RenameConflicts is a transform that renames declarations which prevent identifiers from
diff --git a/src/tint/lang/core/ir/transform/std140.h b/src/tint/lang/core/ir/transform/std140.h
index 4d0cc42..fb3040b 100644
--- a/src/tint/lang/core/ir/transform/std140.h
+++ b/src/tint/lang/core/ir/transform/std140.h
@@ -43,6 +43,7 @@
     core::ir::Capability::kAllowHandleVarsWithoutBindings,
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
     core::ir::Capability::kAllowDuplicateBindings,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// Std140 is a transform that rewrites matrix types in the uniform address space to conform to
diff --git a/src/tint/lang/core/ir/transform/value_to_let.h b/src/tint/lang/core/ir/transform/value_to_let.h
index d44f0d7..c225b61 100644
--- a/src/tint/lang/core/ir/transform/value_to_let.h
+++ b/src/tint/lang/core/ir/transform/value_to_let.h
@@ -52,6 +52,7 @@
     core::ir::Capability::kAllowWorkspacePointerInputToEntryPoint,
     core::ir::Capability::kAllowModuleScopeLets,
     core::ir::Capability::kAllowDuplicateBindings,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// Configuration for ValueToLet transform.
diff --git a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.h b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.h
index 2d0e376..c949773 100644
--- a/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.h
+++ b/src/tint/lang/core/ir/transform/vectorize_scalar_matrix_constructors.h
@@ -44,6 +44,7 @@
     core::ir::Capability::kAllowHandleVarsWithoutBindings,
     core::ir::Capability::kAllowClipDistancesOnF32,
     core::ir::Capability::kAllowDuplicateBindings,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// VectorizeScalarMatrixConstructors is a transform that replaces construct instructions that
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index f96f579..85be9c9 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -1822,6 +1822,15 @@
         return;
     }
 
+    if (!capabilities_.Contains(Capability::kAllowNonCoreTypes)) {
+        // Check for core types (this is a hack to determine if the type is core, non-core types
+        // prefix their names with `lang.`, so we search for a `.` to find non-core)
+        if (root->FriendlyName().find(".") != std::string::npos) {
+            diag() << "non-core types not allowed in core IR";
+            return;
+        }
+    }
+
     if (!validated_types_.Add(ValidatedType{root, ignore_caps})) {
         return;
     }
diff --git a/src/tint/lang/core/ir/validator.h b/src/tint/lang/core/ir/validator.h
index 34ec2cc..36f9ed2 100644
--- a/src/tint/lang/core/ir/validator.h
+++ b/src/tint/lang/core/ir/validator.h
@@ -77,6 +77,8 @@
     kAllowDuplicateBindings,
     /// Allows module scope `var`s to exist without an IO annotation
     kAllowUnannotatedModuleIOVariables,
+    /// Allows non-core types in the IR module
+    kAllowNonCoreTypes,
 };
 
 /// Capabilities is a set of Capability
diff --git a/src/tint/lang/hlsl/ir/member_builtin_call_test.cc b/src/tint/lang/hlsl/ir/member_builtin_call_test.cc
index 5e0bda0..e6ec819 100644
--- a/src/tint/lang/hlsl/ir/member_builtin_call_test.cc
+++ b/src/tint/lang/hlsl/ir/member_builtin_call_test.cc
@@ -82,7 +82,9 @@
         b.Return(func, builtin);
     });
 
-    auto res = core::ir::Validate(mod);
+    auto res = core::ir::Validate(mod, core::ir::Capabilities{
+                                           core::ir::Capability::kAllowNonCoreTypes,
+                                       });
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
@@ -122,7 +124,9 @@
         b.Return(func, builtin);
     });
 
-    auto res = core::ir::Validate(mod);
+    auto res = core::ir::Validate(mod, core::ir::Capabilities{
+                                           core::ir::Capability::kAllowNonCoreTypes,
+                                       });
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
@@ -164,7 +168,9 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod);
+    auto res = core::ir::Validate(mod, core::ir::Capabilities{
+                                           core::ir::Capability::kAllowNonCoreTypes,
+                                       });
     ASSERT_EQ(res, Success);
 }
 
@@ -182,7 +188,9 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod);
+    auto res = core::ir::Validate(mod, core::ir::Capabilities{
+                                           core::ir::Capability::kAllowNonCoreTypes,
+                                       });
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason,
               R"(:7:16 error: Load: expected exactly 1 results, got 0
@@ -219,7 +227,9 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod);
+    auto res = core::ir::Validate(mod, core::ir::Capabilities{
+                                           core::ir::Capability::kAllowNonCoreTypes,
+                                       });
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason,
               R"(:7:17 error: Load: no matching call to 'Load(hlsl.byte_address_buffer<read>)'
@@ -318,7 +328,9 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod);
+    auto res = core::ir::Validate(mod, core::ir::Capabilities{
+                                           core::ir::Capability::kAllowNonCoreTypes,
+                                       });
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
diff --git a/src/tint/lang/hlsl/writer/printer/printer.cc b/src/tint/lang/hlsl/writer/printer/printer.cc
index 3c07554..7255e89 100644
--- a/src/tint/lang/hlsl/writer/printer/printer.cc
+++ b/src/tint/lang/hlsl/writer/printer/printer.cc
@@ -180,6 +180,7 @@
             core::ir::Capability::kAllowVectorElementPointer,
             core::ir::Capability::kAllowClipDistancesOnF32,
             core::ir::Capability::kAllowDuplicateBindings,
+            core::ir::Capability::kAllowNonCoreTypes,
         };
         auto valid = core::ir::ValidateAndDumpIfNeeded(ir_, "hlsl.Printer", capabilities);
         if (valid != Success) {
diff --git a/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc b/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc
index 5b43380..b7b81b4 100644
--- a/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc
@@ -141,6 +141,7 @@
                                           core::ir::Capabilities{
                                               core::ir::Capability::kAllowClipDistancesOnF32,
                                               core::ir::Capability::kAllowDuplicateBindings,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
index 9b513ab..1653808 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
@@ -1938,6 +1938,7 @@
                                           core::ir::Capabilities{
                                               core::ir::Capability::kAllowClipDistancesOnF32,
                                               core::ir::Capability::kAllowDuplicateBindings,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
index 62e1081..d317dcc 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
@@ -6488,6 +6488,10 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, Pack4xI8) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* u = b.Var("u", b.Splat(ty.vec4<i32>(), 2_i));
@@ -6526,6 +6530,10 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, Unpack4xI8) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* u = b.Var("u", 2_u);
@@ -6564,6 +6572,10 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, Pack4xU8) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* u = b.Var("u", b.Splat(ty.vec4<u32>(), 2_u));
@@ -6602,6 +6614,10 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, Unpack4xU8) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* u = b.Var("u", 2_u);
@@ -6715,6 +6731,10 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, Pack4xI8Clamp) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* u = b.Var("u", b.Splat(ty.vec4<i32>(), 2_i));
diff --git a/src/tint/lang/hlsl/writer/raise/change_immediate_to_uniform.cc b/src/tint/lang/hlsl/writer/raise/change_immediate_to_uniform.cc
index fd30748c..96754b2 100644
--- a/src/tint/lang/hlsl/writer/raise/change_immediate_to_uniform.cc
+++ b/src/tint/lang/hlsl/writer/raise/change_immediate_to_uniform.cc
@@ -133,10 +133,12 @@
 
 Result<SuccessType> ChangeImmediateToUniform(core::ir::Module& ir,
                                              const ChangeImmediateToUniformConfig& config) {
-    auto result = ValidateAndDumpIfNeeded(
-        ir, "hlsl.ChangeImmediateToUniform",
-        core::ir::Capabilities{core::ir::Capability::kAllowClipDistancesOnF32,
-                               core::ir::Capability::kAllowDuplicateBindings});
+    auto result = ValidateAndDumpIfNeeded(ir, "hlsl.ChangeImmediateToUniform",
+                                          core::ir::Capabilities{
+                                              core::ir::Capability::kAllowClipDistancesOnF32,
+                                              core::ir::Capability::kAllowDuplicateBindings,
+                                              core::ir::Capability::kAllowNonCoreTypes,
+                                          });
     if (result != Success) {
         return result.Failure();
     }
diff --git a/src/tint/lang/hlsl/writer/raise/decompose_storage_access_test.cc b/src/tint/lang/hlsl/writer/raise/decompose_storage_access_test.cc
index 676e0c1..97db581 100644
--- a/src/tint/lang/hlsl/writer/raise/decompose_storage_access_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/decompose_storage_access_test.cc
@@ -65,6 +65,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessChainFromUnnamedAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* Inner = ty.Struct(mod.symbols.New("Inner"), {
                                                           {mod.symbols.New("c"), ty.f32()},
                                                           {mod.symbols.New("d"), ty.u32()},
@@ -145,6 +149,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessChainFromLetAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* Inner = ty.Struct(mod.symbols.New("Inner"), {
                                                           {mod.symbols.New("c"), ty.f32()},
                                                       });
@@ -226,6 +234,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessRwByteAddressBuffer) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.vec3<f32>()},
@@ -295,6 +307,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessByteAddressBuffer) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                 });
@@ -352,6 +368,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageVector) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec4<f32>, core::Access::kRead>("v");
     var->SetBindingPoint(0, 0);
 
@@ -420,6 +440,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageVectorF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec4<f16>, core::Access::kRead>("v");
     var->SetBindingPoint(0, 0);
 
@@ -483,6 +507,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageMatrix) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat4x4<f32>, core::Access::kRead>("v");
     var->SetBindingPoint(0, 0);
 
@@ -559,6 +587,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageArray) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, array<vec3<f32>, 5>, core::Access::kRead>("v");
     var->SetBindingPoint(0, 0);
 
@@ -640,6 +672,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageArrayWhichCanHaveSizesOtherThenFive) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, array<vec3<f32>, 42>, core::Access::kRead>("v");
     var->SetBindingPoint(0, 0);
 
@@ -721,6 +757,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageStruct) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.f32()},
@@ -798,6 +838,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessStorageNested) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* Inner =
         ty.Struct(mod.symbols.New("Inner"), {
                                                 {mod.symbols.New("s"), ty.mat3x3<f32>()},
@@ -973,6 +1017,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ComplexStaticAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 = ty.Struct(mod.symbols.New("S1"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.vec3<f32>()},
@@ -1080,6 +1128,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ComplexDynamicAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 = ty.Struct(mod.symbols.New("S1"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.vec3<f32>()},
@@ -1202,6 +1254,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ComplexDynamicAccessChainDynamicAccessInMiddle) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 = ty.Struct(mod.symbols.New("S1"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.vec3<f32>()},
@@ -1308,6 +1364,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicStore) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("padding"), ty.vec4<f32>()},
                                                     {mod.symbols.New("a"), ty.atomic<i32>()},
@@ -1372,6 +1432,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicStoreDynamicAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("padding"), ty.vec4<f32>()},
@@ -1452,6 +1516,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicStoreDirect) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var("v", storage, ty.atomic<i32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1496,6 +1564,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicLoad) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("padding"), ty.vec4<f32>()},
                                                     {mod.symbols.New("a"), ty.atomic<i32>()},
@@ -1563,6 +1635,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicLoadDynamicAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("padding"), ty.vec4<f32>()},
@@ -1646,6 +1722,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicLoadDirect) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var("v", storage, ty.atomic<i32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1693,6 +1773,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicSub) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("padding"), ty.vec4<f32>()},
                                                     {mod.symbols.New("a"), ty.atomic<i32>()},
@@ -1761,6 +1845,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicSubDynamicAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("padding"), ty.vec4<f32>()},
@@ -1845,6 +1933,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicSubDirect) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var("v", storage, ty.atomic<i32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1893,6 +1985,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicCompareExchangeWeak) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("padding"), ty.vec4<f32>()},
                                                     {mod.symbols.New("a"), ty.atomic<i32>()},
@@ -1974,6 +2070,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicCompareExchangeWeakDynamicAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* S1 =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("padding"), ty.vec4<f32>()},
@@ -2070,6 +2170,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageAtomicCompareExchangeWeakDirect) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var("v", storage, ty.atomic<i32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -2140,6 +2244,10 @@
 }
 using DecomposeBuiltinAtomic = core::ir::transform::TransformTestWithParam<AtomicData>;
 TEST_P(DecomposeBuiltinAtomic, IndirectAccess) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto params = GetParam();
 
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
@@ -2211,6 +2319,10 @@
 }
 
 TEST_P(DecomposeBuiltinAtomic, DirectAccess) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto param = GetParam();
 
     auto* var = b.Var("v", storage, ty.atomic<u32>(), core::Access::kReadWrite);
@@ -2261,6 +2373,10 @@
 }
 
 TEST_P(DecomposeBuiltinAtomic, DynamicAccessChain) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto param = GetParam();
 
     auto* S1 = ty.Struct(mod.symbols.New("S1"),
@@ -2356,6 +2472,10 @@
                                "InterlockedExchange"}));
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreVecF32) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec4<f32>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -2411,6 +2531,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreScalar) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, f32, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2453,6 +2577,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreScalarF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, f16, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2494,6 +2622,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreVectorElement) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec3<f32>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2536,6 +2668,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreVectorElementF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec3<f16>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2577,6 +2713,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreVector) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec3<f32>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2619,6 +2759,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreVectorF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, vec3<f16>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2661,6 +2805,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreMatrixElement) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat2x3<f32>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2705,6 +2853,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreMatrixElementF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat2x3<f16>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2748,6 +2900,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreMatrixColumn) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat2x3<f32>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2792,6 +2948,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreMatrixColumnF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat2x3<f16>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2835,6 +2995,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreMatrix) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat2x3<f32>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2889,6 +3053,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreMatrixF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, mat2x3<f16>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2941,6 +3109,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreArrayElement) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, array<f32, 5>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -2984,6 +3156,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreArrayElementF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, array<f16, 5>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -3026,6 +3202,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreArray) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* var = b.Var<storage, array<vec3<f32>, 5>, core::Access::kReadWrite>("v");
     var->SetBindingPoint(0, 0);
 
@@ -3098,6 +3278,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreStructMember) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.f32()},
@@ -3156,6 +3340,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreStructMemberF16) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.f16()},
@@ -3213,6 +3401,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreStructNested) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* Inner =
         ty.Struct(mod.symbols.New("Inner"), {
                                                 {mod.symbols.New("s"), ty.mat3x3<f32>()},
@@ -3301,6 +3493,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreStruct) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* Inner = ty.Struct(mod.symbols.New("Inner"), {
                                                           {mod.symbols.New("s"), ty.f32()},
                                                           {mod.symbols.New("t"), ty.vec3<f32>()},
@@ -3426,6 +3622,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StoreStructComplex) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* Inner =
         ty.Struct(mod.symbols.New("Inner"), {
                                                 {mod.symbols.New("s"), ty.mat3x3<f32>()},
@@ -3595,6 +3795,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ArrayLengthDirect) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = b.Var("sb", ty.ptr<storage, array<i32>>());
     sb->SetBindingPoint(0, 0);
     b.ir.root_block->Append(sb);
@@ -3641,6 +3845,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ArrayLengthInStruct) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("x"), ty.i32()},
@@ -3706,6 +3914,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ArrayLengthOfStruct) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("f"), ty.f32()},
                                                 });
@@ -3764,6 +3976,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ArrayLengthArrayOfArrayOfStruct) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("f"), ty.f32()},
                                                 });
@@ -3821,6 +4037,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ArrayLengthMultiple) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = b.Var("sb", ty.ptr<storage, array<i32>>());
     sb->SetBindingPoint(0, 0);
     b.ir.root_block->Append(sb);
@@ -3883,6 +4103,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, ArrayLengthMultipleStorageBuffers) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* SB1 =
         ty.Struct(mod.symbols.New("SB1"), {
                                               {mod.symbols.New("x"), ty.i32()},
@@ -3993,6 +4217,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, AccessChainReused) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb = ty.Struct(mod.symbols.New("SB"), {
                                                     {mod.symbols.New("a"), ty.i32()},
                                                     {mod.symbols.New("b"), ty.vec3<f32>()},
@@ -4061,6 +4289,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, Determinism_MultipleUsesOfLetFromVar) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("a"), ty.array<vec4<f32>, 2>()},
@@ -4196,6 +4428,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, Determinism_MultipleUsesOfLetFromAccess) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("a"), ty.array<vec4<f32>, 2>()},
@@ -4332,6 +4568,10 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, Determinism_MultipleUsesOfAccess) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* sb =
         ty.Struct(mod.symbols.New("SB"), {
                                              {mod.symbols.New("a"), ty.array<vec4<f32>, 2>()},
diff --git a/src/tint/lang/hlsl/writer/raise/decompose_uniform_access.cc b/src/tint/lang/hlsl/writer/raise/decompose_uniform_access.cc
index aba068c..65b4c49 100644
--- a/src/tint/lang/hlsl/writer/raise/decompose_uniform_access.cc
+++ b/src/tint/lang/hlsl/writer/raise/decompose_uniform_access.cc
@@ -619,6 +619,7 @@
                                           core::ir::Capabilities{
                                               core::ir::Capability::kAllowClipDistancesOnF32,
                                               core::ir::Capability::kAllowDuplicateBindings,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/hlsl/writer/raise/pixel_local_test.cc b/src/tint/lang/hlsl/writer/raise/pixel_local_test.cc
index a462370..b1511ad 100644
--- a/src/tint/lang/hlsl/writer/raise/pixel_local_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/pixel_local_test.cc
@@ -149,6 +149,10 @@
 }
 
 TEST_F(HlslWriterPixelLocalTest, UsedInEntry) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto r = OneArgFunc();
     b.Append(r.func->Block(), [&] {
         auto* access = b.Access(ty.ptr<pixel_local>(ty.u32()), r.pl, 0_u);
@@ -230,6 +234,10 @@
 }
 
 TEST_F(HlslWriterPixelLocalTest, UsedInNonEntry) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto r = OneArgFunc();
     auto* func2 = b.Function("foo", ty.void_());
     b.Append(func2->Block(), [&] {
@@ -328,6 +336,10 @@
 }
 
 TEST_F(HlslWriterPixelLocalTest, UsedInNonEntryViaPointer) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto r = OneArgFunc();
     auto* func2 = b.Function("foo", ty.void_());
     b.Append(func2->Block(), [&] {
@@ -428,6 +440,10 @@
 }
 
 TEST_F(HlslWriterPixelLocalTest, MultipleInputBuiltins) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto r = OneArgFunc(/*multiple_builtins*/ true);
     b.Append(r.func->Block(), [&] {
         auto* access = b.Access(ty.ptr<pixel_local>(ty.u32()), r.pl, 0_u);
@@ -513,6 +529,10 @@
 }
 
 TEST_F(HlslWriterPixelLocalTest, MultipleMembers) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto r = ThreeArgFunc();
     b.Append(r.func->Block(), [&] {
         auto* access = b.Access(ty.ptr<pixel_local>(ty.u32()), r.pl, 0_u);
@@ -620,6 +640,10 @@
 }
 
 TEST_F(HlslWriterPixelLocalTest, MultipleMembers_MismatchedTypes) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto r = ThreeArgFunc();
     b.Append(r.func->Block(), [&] {
         auto* access = b.Access(ty.ptr<pixel_local>(ty.u32()), r.pl, 0_u);
diff --git a/src/tint/lang/hlsl/writer/raise/promote_initializers.h b/src/tint/lang/hlsl/writer/raise/promote_initializers.h
index ec4d79c..596a367 100644
--- a/src/tint/lang/hlsl/writer/raise/promote_initializers.h
+++ b/src/tint/lang/hlsl/writer/raise/promote_initializers.h
@@ -43,6 +43,7 @@
     core::ir::Capability::kAllowVectorElementPointer,
     core::ir::Capability::kAllowClipDistancesOnF32,
     core::ir::Capability::kAllowDuplicateBindings,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// PromoteInitializers is a transform that moves inline struct and array initializers to a `let`
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index 84e2c43..401b00c 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -133,6 +133,7 @@
                 core::ir::Capability::kAllowAnyLetType,
                 core::ir::Capability::kAllowModuleScopeLets,
                 core::ir::Capability::kAllowWorkspacePointerInputToEntryPoint,
+                core::ir::Capability::kAllowNonCoreTypes,
             });
         if (valid != Success) {
             return std::move(valid.Failure());
diff --git a/src/tint/lang/msl/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/msl/writer/raise/builtin_polyfill_test.cc
index f09ce3f..44f2071 100644
--- a/src/tint/lang/msl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/msl/writer/raise/builtin_polyfill_test.cc
@@ -2295,6 +2295,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleBias) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2332,6 +2336,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleBias_Array) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -2408,6 +2416,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleCompareLevel) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2446,6 +2458,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleCompareLevel_WithOffset) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2485,6 +2501,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleGrad_2d) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2562,6 +2582,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleGrad_2dArray) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -2604,6 +2628,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleGrad_3d) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k3d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec3<f32>());
@@ -2643,6 +2671,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleGrad_Cube) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::kCube, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -2683,6 +2715,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleGrad_WithOffset) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2759,6 +2795,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleLevel_2d) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2796,6 +2836,10 @@
 }
 
 TEST_F(MslWriter_BuiltinPolyfillTest, TextureSampleLevel_Array) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
diff --git a/src/tint/lang/spirv/reader/lower/atomics.cc b/src/tint/lang/spirv/reader/lower/atomics.cc
index 1777c16..1bcceba 100644
--- a/src/tint/lang/spirv/reader/lower/atomics.cc
+++ b/src/tint/lang/spirv/reader/lower/atomics.cc
@@ -550,6 +550,7 @@
     auto result = ValidateAndDumpIfNeeded(ir, "spirv.Atomics",
                                           core::ir::Capabilities{
                                               core::ir::Capability::kAllowOverrides,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/spirv/reader/lower/builtins.cc b/src/tint/lang/spirv/reader/lower/builtins.cc
index 48c7c18..f2ab369 100644
--- a/src/tint/lang/spirv/reader/lower/builtins.cc
+++ b/src/tint/lang/spirv/reader/lower/builtins.cc
@@ -1167,6 +1167,7 @@
     auto result = ValidateAndDumpIfNeeded(ir, "spirv.Builtins",
                                           core::ir::Capabilities{
                                               core::ir::Capability::kAllowOverrides,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/spirv/reader/lower/shader_io.cc b/src/tint/lang/spirv/reader/lower/shader_io.cc
index f6d2367..ec45746 100644
--- a/src/tint/lang/spirv/reader/lower/shader_io.cc
+++ b/src/tint/lang/spirv/reader/lower/shader_io.cc
@@ -634,6 +634,7 @@
                                               core::ir::Capability::kAllowMultipleEntryPoints,
                                               core::ir::Capability::kAllowOverrides,
                                               core::ir::Capability::kAllowPhonyInstructions,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/spirv/reader/lower/texture.cc b/src/tint/lang/spirv/reader/lower/texture.cc
index 537f821..acba979 100644
--- a/src/tint/lang/spirv/reader/lower/texture.cc
+++ b/src/tint/lang/spirv/reader/lower/texture.cc
@@ -737,6 +737,7 @@
     auto result = ValidateAndDumpIfNeeded(ir, "spirv.Texture",
                                           core::ir::Capabilities{
                                               core::ir::Capability::kAllowOverrides,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
index 969e83e..ae0c258 100644
--- a/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
+++ b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
@@ -156,6 +156,7 @@
                                               core::ir::Capability::kAllowOverrides,
                                               core::ir::Capability::kAllowVectorElementPointer,
                                               core::ir::Capability::kAllowPhonyInstructions,
+                                              core::ir::Capability::kAllowNonCoreTypes,
                                           });
     if (result != Success) {
         return result.Failure();
diff --git a/src/tint/lang/spirv/reader/parser/helper_test.h b/src/tint/lang/spirv/reader/parser/helper_test.h
index ee18874..d12b784 100644
--- a/src/tint/lang/spirv/reader/parser/helper_test.h
+++ b/src/tint/lang/spirv/reader/parser/helper_test.h
@@ -79,6 +79,7 @@
                                         core::ir::Capability::kAllowOverrides,
                                         core::ir::Capability::kAllowPhonyInstructions,
                                         core::ir::Capability::kAllowVectorElementPointer,
+                                        core::ir::Capability::kAllowNonCoreTypes,
                                     });
         if (validated != Success) {
             return validated.Failure();
diff --git a/src/tint/lang/spirv/writer/printer/printer.h b/src/tint/lang/spirv/writer/printer/printer.h
index 6fc55d0..074429d 100644
--- a/src/tint/lang/spirv/writer/printer/printer.h
+++ b/src/tint/lang/spirv/writer/printer/printer.h
@@ -43,6 +43,7 @@
 // The capabilities that might be needed due to raising.
 const core::ir::Capabilities kPrinterCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// @returns the generated SPIR-V instructions on success, or failure
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
index f881bd1..7a903e1 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
@@ -1287,6 +1287,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
     auto* lod = b.FunctionParam("lod", ty.i32());
@@ -1324,6 +1326,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_BindingArray_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* sampled_texture = ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32());
     auto* arr = b.FunctionParam("arr", ty.binding_array(sampled_texture, 2));
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
@@ -1365,6 +1369,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
@@ -1406,6 +1412,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_2DArray_IndexDifferentType) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
@@ -1448,6 +1456,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_Multisampled2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.multisampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
@@ -1486,6 +1496,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_Depth2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
     auto* lod = b.FunctionParam("lod", ty.i32());
@@ -1524,6 +1536,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_Storage) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kR32Uint;
     auto* t = b.FunctionParam("t", ty.storage_texture(core::type::TextureDimension::k2d, format,
                                                       core::Access::kReadWrite));
@@ -1562,6 +1576,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureLoad_Storage_Vulkan) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kR32Uint;
     auto* t = b.FunctionParam("t", ty.storage_texture(core::type::TextureDimension::k2d, format,
                                                       core::Access::kReadWrite));
@@ -1600,6 +1616,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSample_1D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k1d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.f32());
@@ -1638,6 +1656,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSample_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -1676,6 +1696,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSample_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -1715,6 +1737,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSample_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -1758,6 +1782,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleBias_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -1798,6 +1824,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleBias_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -1838,6 +1866,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleBias_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -1882,6 +1912,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleCompare_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -1920,6 +1952,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleCompare_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -1960,6 +1994,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleCompare_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2dArray));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2003,6 +2039,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleCompareLevel_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2043,6 +2081,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleCompareLevel_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2083,6 +2123,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleCompareLevel_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2dArray));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2126,6 +2168,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleGrad_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2167,6 +2211,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleGrad_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2208,6 +2254,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleGrad_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -2253,6 +2301,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleLevel_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2293,6 +2343,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleLevel_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2333,6 +2385,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureSampleLevel_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -2377,6 +2431,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGather_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* component = b.FunctionParam("component", ty.i32());
@@ -2417,6 +2473,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGather_2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* component = b.FunctionParam("component", ty.i32());
@@ -2457,6 +2515,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGather_2DArray_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* s = b.FunctionParam("s", ty.sampler());
@@ -2501,6 +2561,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGather_Depth2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2539,6 +2601,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGatherCompare_Depth2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2579,6 +2643,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGatherCompare_Depth2D_Offset) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2619,6 +2685,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureGatherCompare_Depth2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2dArray));
     auto* s = b.FunctionParam("s", ty.comparison_sampler());
     auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
@@ -2662,6 +2730,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureStore_2D) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kR32Uint;
     auto* t = b.FunctionParam(
         "t", ty.storage_texture(core::type::TextureDimension::k2d, format, core::Access::kWrite));
@@ -2701,6 +2771,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureStore_2D_Vulkan) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kR32Uint;
     auto* t = b.FunctionParam("t", ty.storage_texture(core::type::TextureDimension::k2d, format,
                                                       core::Access::kReadWrite));
@@ -2740,6 +2812,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureStore_2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kRgba8Sint;
     auto* t = b.FunctionParam("t", ty.storage_texture(core::type::TextureDimension::k2dArray,
                                                       format, core::Access::kWrite));
@@ -2781,6 +2855,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureStore_2DArray_IndexDifferentType) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kRgba32Uint;
     auto* t = b.FunctionParam("t", ty.storage_texture(core::type::TextureDimension::k2dArray,
                                                       format, core::Access::kWrite));
@@ -2823,6 +2899,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureDimensions_2D_ImplicitLod) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* func = b.Function("foo", ty.vec2<u32>());
     func->SetParams({t});
@@ -2858,6 +2936,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureDimensions_2D_ExplicitLod) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* lod = b.FunctionParam("lod", ty.i32());
     auto* func = b.Function("foo", ty.vec2<u32>());
@@ -2894,6 +2974,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureDimensions_2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* func = b.Function("foo", ty.vec2<u32>());
@@ -2931,6 +3013,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureDimensions_Multisampled) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.multisampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* func = b.Function("foo", ty.vec2<u32>());
@@ -2967,6 +3051,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_1d) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k1d, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3002,6 +3088,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_2d) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2d, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3037,6 +3125,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_2d_array) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
@@ -3073,6 +3163,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_3d) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k3d, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3108,6 +3200,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_cube) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::kCube, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
@@ -3144,6 +3238,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_CubeArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam(
         "t", ty.sampled_texture(core::type::TextureDimension::kCubeArray, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
@@ -3180,6 +3276,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_depth_2d) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2d));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3215,6 +3313,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_depth_2dArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2dArray));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3250,6 +3350,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_depth_cube) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::kCube));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3285,6 +3387,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLevels_depth_CubeArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::kCubeArray));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3320,6 +3424,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumSamples_ms_2d) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.multisampled_texture(core::type::TextureDimension::k2d, ty.i32()));
     auto* func = b.Function("foo", ty.u32());
@@ -3356,6 +3462,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumSamples_depth_ms_2d) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.depth_multisampled_texture(core::type::TextureDimension::k2d));
     auto* func = b.Function("foo", ty.u32());
@@ -3392,6 +3500,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLayers_2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t =
         b.FunctionParam("t", ty.sampled_texture(core::type::TextureDimension::k2dArray, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
@@ -3429,6 +3539,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLayers_CubeArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam(
         "t", ty.sampled_texture(core::type::TextureDimension::kCubeArray, ty.f32()));
     auto* func = b.Function("foo", ty.u32());
@@ -3466,6 +3578,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLayers_Depth2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::k2dArray));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3502,6 +3616,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLayers_DepthCubeArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.depth_texture(core::type::TextureDimension::kCubeArray));
     auto* func = b.Function("foo", ty.u32());
     func->SetParams({t});
@@ -3538,6 +3654,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, TextureNumLayers_Storage2DArray) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto format = core::TexelFormat::kR32Float;
     auto* t = b.FunctionParam("t", ty.storage_texture(core::type::TextureDimension::k2dArray,
                                                       format, core::Access::kWrite));
@@ -3647,6 +3765,8 @@
 }
 
 TEST_F(SpirvWriter_BuiltinPolyfillTest, InputAttachmentLoad) {
+    capabilities = core::ir::Capability::kAllowNonCoreTypes;
+
     auto* t = b.FunctionParam("t", ty.input_attachment(ty.f32()));
     auto* func = b.Function("foo", ty.vec4<f32>());
     func->SetParams({t});
diff --git a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.h b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.h
index 8d387c3..111a7fa 100644
--- a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.h
+++ b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.h
@@ -41,6 +41,7 @@
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kExpandImplicitSplatsCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// ExpandImplicitSplats is a transform that expands implicit vector splat operands in construct
diff --git a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h
index 8af505a..79148fa 100644
--- a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h
+++ b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.h
@@ -44,6 +44,7 @@
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kForkExplicitLayoutTypesCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// ForkExplicitLayoutTypes is a transform that forks array and structures types that are shared
diff --git a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc
index f704bd7..d01fa9c 100644
--- a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc
+++ b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types_test.cc
@@ -194,6 +194,10 @@
 // Test that we always modify arrays that require explicit layout decorations, since the type is
 // used to signal to the printer that layout decorations are required.
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Array_InHostShareable_NotShared) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* buffer = b.Var("buffer", ty.ptr<storage, array<u32, 4>>());
     buffer->SetBindingPoint(0, 0);
     mod.root_block->Append(buffer);
@@ -555,6 +559,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Storage_SharedWithInOut) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* array = ty.array<u32, 1>();
     b.Append(mod.root_block, [&] {
         auto* buffer = b.Var("buffer", ty.ptr(storage, array));
@@ -1945,6 +1953,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, LoadFromStorage_Array_Shared) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* buffer = b.Var("buffer", ty.ptr<storage, array<u32, 4>>());
     buffer->SetBindingPoint(0, 0);
     mod.root_block->Append(buffer);
@@ -2019,6 +2031,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, LoadFromStorage_NestedArray_Shared) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* buffer = b.Var("buffer", ty.ptr<storage, array<array<u32, 4>, 3>>());
     buffer->SetBindingPoint(0, 0);
     mod.root_block->Append(buffer);
@@ -2122,6 +2138,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, StoreToStorage_Array_Shared) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* buffer = b.Var("buffer", ty.ptr<storage, array<u32, 4>, read_write>());
     buffer->SetBindingPoint(0, 0);
     mod.root_block->Append(buffer);
@@ -2199,6 +2219,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, StoreToStorage_NestedArray_Shared) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* buffer = b.Var("buffer", ty.ptr<storage, array<array<u32, 4>, 3>, read_write>());
     buffer->SetBindingPoint(0, 0);
     mod.root_block->Append(buffer);
@@ -2305,6 +2329,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedArray_UsesViaLet) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* array = ty.array<u32, 4>();
     auto* buffer = b.Var("buffer", ty.ptr(storage, array));
     buffer->SetBindingPoint(0, 0);
@@ -2416,6 +2444,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedArray_AccessScalarMember) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* array = ty.array<u32, 4>();
     auto* buffer = b.Var("buffer", ty.ptr(storage, array, read_write));
     buffer->SetBindingPoint(0, 0);
@@ -2470,6 +2502,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedArray_AccessNestedArray) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* inner = ty.array<u32, 4>();
     auto* outer = ty.array(inner, 4);
     auto* buffer = b.Var("buffer", ty.ptr(storage, outer, read_write));
@@ -2583,6 +2619,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, SharedArray_MultipleVars) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* array = ty.array<u32, 4>();
     auto* buffer_0 = b.Var("buffer_0", ty.ptr(storage, array));
     auto* buffer_1 = b.Var("buffer_1", ty.ptr(storage, array));
@@ -2683,6 +2723,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, ArrayLength) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* structure =
         ty.Struct(mod.symbols.New("MyStruct"), {
                                                    {mod.symbols.New("i"), ty.u32()},
@@ -2891,6 +2935,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Spv14_CopyLogical_ArrayRemoveLayout) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* array_ty = ty.array(ty.u32(), 4, 4);
     auto* ssbo = b.Var("ssbo", ty.ptr(storage, array_ty));
     ssbo->SetBindingPoint(0, 0);
@@ -2944,6 +2992,10 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Spv14_CopyLogical_ArrayAddLayout) {
+    capabilities = core::ir::Capabilities{
+        core::ir::Capability::kAllowNonCoreTypes,
+    };
+
     auto* array_ty = ty.array(ty.u32(), 4, 4);
     auto* ssbo = b.Var("ssbo", ty.ptr(storage, array_ty));
     ssbo->SetBindingPoint(0, 0);
diff --git a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h
index d1fa97f..fc7e3c5 100644
--- a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h
+++ b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h
@@ -41,6 +41,7 @@
 // The capabilities that the transform can support.
 const core::ir::Capabilities kHandleMatrixArithmeticCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// HandleMatrixArithmetic is a transform that converts arithmetic instruction that use matrix into
diff --git a/src/tint/lang/spirv/writer/raise/merge_return.cc b/src/tint/lang/spirv/writer/raise/merge_return.cc
index c7a03f5..9373b7e 100644
--- a/src/tint/lang/spirv/writer/raise/merge_return.cc
+++ b/src/tint/lang/spirv/writer/raise/merge_return.cc
@@ -43,6 +43,7 @@
 // The capabilities that the transform can support.
 const core::ir::Capabilities kMergeReturnCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// PIMPL state for the transform, for a single function.
diff --git a/src/tint/lang/spirv/writer/raise/remove_unreachable_in_loop_continuing.h b/src/tint/lang/spirv/writer/raise/remove_unreachable_in_loop_continuing.h
index 5c04733..02a3c53 100644
--- a/src/tint/lang/spirv/writer/raise/remove_unreachable_in_loop_continuing.h
+++ b/src/tint/lang/spirv/writer/raise/remove_unreachable_in_loop_continuing.h
@@ -41,7 +41,9 @@
 // The capabilities that the transform can support.
 const core::ir::Capabilities kRemoveUnreachableInLoopContinuingCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
+
 /// RemoveUnreachableInLoopContinuing is a transform that replaces unreachable statements that are
 /// nested inside a loop continuing block, as SPIR-V's structured control flow rules prohibit this.
 /// @param module the module to transform
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.h b/src/tint/lang/spirv/writer/raise/shader_io.h
index 629413b..3064ac1 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.h
+++ b/src/tint/lang/spirv/writer/raise/shader_io.h
@@ -43,6 +43,7 @@
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kShaderIOCapabilities{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// ShaderIOConfig describes the set of configuration options for the ShaderIO transform.
diff --git a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.h b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.h
index 5f9363e..f8e122a 100644
--- a/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.h
+++ b/src/tint/lang/spirv/writer/raise/var_for_dynamic_index.h
@@ -41,6 +41,7 @@
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kVarForDynamicIndex{
     core::ir::Capability::kAllowAnyInputAttachmentIndexType,
+    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// VarForDynamicIndex is a transform that copies array and matrix values that are dynamically