[ir] Move AllowNonCoreTypes to a property

The SPIR-V parser adds the property, and it is removed at the end of
spirv.lower. The WGSL ir-to-program step rejects the property.

Many other backend transforms also add the property.

Bug: 512904070
Change-Id: I30ecf24fac8682721e045872822d4373eb229e60
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/313115
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/module.h b/src/tint/lang/core/ir/module.h
index a2ef70c..278b477 100644
--- a/src/tint/lang/core/ir/module.h
+++ b/src/tint/lang/core/ir/module.h
@@ -72,6 +72,8 @@
     kAllowMslEntryPointInterface,
     /// Allows multiple entry points in the module.
     kAllowMultipleEntryPoints,
+    /// Allows non-core types to be used.
+    kAllowNonCoreTypes,
     /// Allow overrides
     kAllowOverrides,
     /// Allows phony assignment instructions to be used.
@@ -113,6 +115,7 @@
         CASE(AllowModuleScopeLets);
         CASE(AllowMslEntryPointInterface);
         CASE(AllowMultipleEntryPoints);
+        CASE(AllowNonCoreTypes);
         CASE(AllowOverrides);
         CASE(AllowPhonyInstructions);
         CASE(AllowPointSizeBuiltin);
diff --git a/src/tint/lang/core/ir/transform/array_length_from.h b/src/tint/lang/core/ir/transform/array_length_from.h
index 58413fe..a9c79c8 100644
--- a/src/tint/lang/core/ir/transform/array_length_from.h
+++ b/src/tint/lang/core/ir/transform/array_length_from.h
@@ -46,7 +46,6 @@
 const Capabilities kArrayLengthCapabilities{
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
-    Capability::kAllowNonCoreTypes,
 };
 
 /// The result of running the ArrayLengthFromUniform transform.
diff --git a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.h b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.h
index 4ddf2d3..0e07aad 100644
--- a/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.h
+++ b/src/tint/lang/core/ir/transform/bgra8unorm_polyfill.h
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kBgra8UnormPolyfillCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
 };
 
diff --git a/src/tint/lang/core/ir/transform/binary_polyfill.h b/src/tint/lang/core/ir/transform/binary_polyfill.h
index 8010c82..161c5de 100644
--- a/src/tint/lang/core/ir/transform/binary_polyfill.h
+++ b/src/tint/lang/core/ir/transform/binary_polyfill.h
@@ -41,7 +41,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kBinaryPolyfillCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/block_decorated_structs.h b/src/tint/lang/core/ir/transform/block_decorated_structs.h
index 5c4f920..9b5343a 100644
--- a/src/tint/lang/core/ir/transform/block_decorated_structs.h
+++ b/src/tint/lang/core/ir/transform/block_decorated_structs.h
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kBlockDecoratedStructsCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/builtin_polyfill.h b/src/tint/lang/core/ir/transform/builtin_polyfill.h
index f90541a..64ee082 100644
--- a/src/tint/lang/core/ir/transform/builtin_polyfill.h
+++ b/src/tint/lang/core/ir/transform/builtin_polyfill.h
@@ -41,7 +41,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kBuiltinPolyfillCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/builtin_scalarize.h b/src/tint/lang/core/ir/transform/builtin_scalarize.h
index 2f3c437..d03c02b 100644
--- a/src/tint/lang/core/ir/transform/builtin_scalarize.h
+++ b/src/tint/lang/core/ir/transform/builtin_scalarize.h
@@ -44,7 +44,6 @@
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
     core::ir::Capability::kAllow64BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// The scalarizer configuration options
diff --git a/src/tint/lang/core/ir/transform/change_immediate_to_uniform.cc b/src/tint/lang/core/ir/transform/change_immediate_to_uniform.cc
index facb6ea..3fddac3 100644
--- a/src/tint/lang/core/ir/transform/change_immediate_to_uniform.cc
+++ b/src/tint/lang/core/ir/transform/change_immediate_to_uniform.cc
@@ -151,7 +151,6 @@
                           core::ir::Capabilities{
                               core::ir::Capability::kAllow8BitIntegers,
                               core::ir::Capability::kAllow16BitIntegers,
-                              core::ir::Capability::kAllowNonCoreTypes,
                           },
                           "before core.ChangeImmediateToUniform");
 
diff --git a/src/tint/lang/core/ir/transform/combine_access_instructions.cc b/src/tint/lang/core/ir/transform/combine_access_instructions.cc
index f8deb2f..4e44add 100644
--- a/src/tint/lang/core/ir/transform/combine_access_instructions.cc
+++ b/src/tint/lang/core/ir/transform/combine_access_instructions.cc
@@ -82,7 +82,6 @@
 Result<SuccessType> CombineAccessInstructions(Module& ir) {
     core::ir::AssertValid(ir,
                           core::ir::Capabilities{
-                              core::ir::Capability::kAllowNonCoreTypes,
                               core::ir::Capability::kAllow8BitIntegers,
                           },
                           "before core.CombineAccessInstructions");
diff --git a/src/tint/lang/core/ir/transform/conversion_polyfill.h b/src/tint/lang/core/ir/transform/conversion_polyfill.h
index 56397b1..1e3ac23 100644
--- a/src/tint/lang/core/ir/transform/conversion_polyfill.h
+++ b/src/tint/lang/core/ir/transform/conversion_polyfill.h
@@ -41,7 +41,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kConversionPolyfillCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
 };
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 c72ec80..4d38757 100644
--- a/src/tint/lang/core/ir/transform/dead_code_elimination.h
+++ b/src/tint/lang/core/ir/transform/dead_code_elimination.h
@@ -41,7 +41,6 @@
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kDeadCodeEliminationCapabilities{
     core::ir::Capability::kAllowUnannotatedModuleIOVariables,
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
 
diff --git a/src/tint/lang/core/ir/transform/decompose_access.cc b/src/tint/lang/core/ir/transform/decompose_access.cc
index 90902e6..adf07a2c 100644
--- a/src/tint/lang/core/ir/transform/decompose_access.cc
+++ b/src/tint/lang/core/ir/transform/decompose_access.cc
@@ -1536,7 +1536,6 @@
                           core::ir::Capabilities{
                               core::ir::Capability::kAllow8BitIntegers,
                               core::ir::Capability::kAllow16BitIntegers,
-                              core::ir::Capability::kAllowNonCoreTypes,
                               core::ir::Capability::kLoosenValidationForShaderIO,
                           },
                           "before core.DecomposeAccess");
diff --git a/src/tint/lang/core/ir/transform/demote_to_helper.h b/src/tint/lang/core/ir/transform/demote_to_helper.h
index 5d65d4d..131fe75 100644
--- a/src/tint/lang/core/ir/transform/demote_to_helper.h
+++ b/src/tint/lang/core/ir/transform/demote_to_helper.h
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kDemoteToHelperCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/direct_variable_access.h b/src/tint/lang/core/ir/transform/direct_variable_access.h
index fc8a83d..7762ad4 100644
--- a/src/tint/lang/core/ir/transform/direct_variable_access.h
+++ b/src/tint/lang/core/ir/transform/direct_variable_access.h
@@ -41,7 +41,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kDirectVariableAccessCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/multiplanar_external_texture.h b/src/tint/lang/core/ir/transform/multiplanar_external_texture.h
index db78708..10e416a 100644
--- a/src/tint/lang/core/ir/transform/multiplanar_external_texture.h
+++ b/src/tint/lang/core/ir/transform/multiplanar_external_texture.h
@@ -41,7 +41,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kMultiplanarExternalTextureCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/prepare_immediate_data.cc b/src/tint/lang/core/ir/transform/prepare_immediate_data.cc
index 0f47667..2de07aa 100644
--- a/src/tint/lang/core/ir/transform/prepare_immediate_data.cc
+++ b/src/tint/lang/core/ir/transform/prepare_immediate_data.cc
@@ -158,7 +158,6 @@
                           core::ir::Capabilities{
                               core::ir::Capability::kAllow8BitIntegers,
                               core::ir::Capability::kAllow16BitIntegers,
-                              core::ir::Capability::kAllowNonCoreTypes,
                           },
                           "before core.PrepareImmediateData");
 
diff --git a/src/tint/lang/core/ir/transform/preserve_padding.h b/src/tint/lang/core/ir/transform/preserve_padding.h
index 395bf61..21cf57a 100644
--- a/src/tint/lang/core/ir/transform/preserve_padding.h
+++ b/src/tint/lang/core/ir/transform/preserve_padding.h
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kPreservePaddingCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
 };
 
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 e8a0168..b27f9a8 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
@@ -124,7 +124,6 @@
                           core::ir::Capabilities{
                               core::ir::Capability::kAllow8BitIntegers,
                               core::ir::Capability::kAllow16BitIntegers,
-                              core::ir::Capability::kAllowNonCoreTypes,
                           },
                           "before core.RemoveContinueInSwitch");
 
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 0ce392a..6013b81 100644
--- a/src/tint/lang/core/ir/transform/remove_terminator_args.h
+++ b/src/tint/lang/core/ir/transform/remove_terminator_args.h
@@ -40,8 +40,9 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kRemoveTerminatorArgsCapabilities{
-    core::ir::Capability::kAllow8BitIntegers,           core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllow64BitIntegers,          core::ir::Capability::kAllowNonCoreTypes,
+    core::ir::Capability::kAllow8BitIntegers,
+    core::ir::Capability::kAllow16BitIntegers,
+    core::ir::Capability::kAllow64BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
 
diff --git a/src/tint/lang/core/ir/transform/remove_uniform_vector_component_loads.cc b/src/tint/lang/core/ir/transform/remove_uniform_vector_component_loads.cc
index e66b6f0..74c0e3c 100644
--- a/src/tint/lang/core/ir/transform/remove_uniform_vector_component_loads.cc
+++ b/src/tint/lang/core/ir/transform/remove_uniform_vector_component_loads.cc
@@ -80,7 +80,6 @@
 Result<SuccessType> RemoveUniformVectorComponentLoads(core::ir::Module& ir) {
     core::ir::AssertValid(ir,
                           core::ir::Capabilities{
-                              core::ir::Capability::kAllowNonCoreTypes,
                               core::ir::Capability::kAllow8BitIntegers,
                               core::ir::Capability::kAllow16BitIntegers,
                           },
diff --git a/src/tint/lang/core/ir/transform/rename_conflicts.h b/src/tint/lang/core/ir/transform/rename_conflicts.h
index f41fb77..bd292b5 100644
--- a/src/tint/lang/core/ir/transform/rename_conflicts.h
+++ b/src/tint/lang/core/ir/transform/rename_conflicts.h
@@ -40,8 +40,9 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kRenameConflictsCapabilities{
-    core::ir::Capability::kAllow8BitIntegers,           core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllow64BitIntegers,          core::ir::Capability::kAllowNonCoreTypes,
+    core::ir::Capability::kAllow8BitIntegers,
+    core::ir::Capability::kAllow16BitIntegers,
+    core::ir::Capability::kAllow64BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
 
diff --git a/src/tint/lang/core/ir/transform/signed_integer_polyfill.cc b/src/tint/lang/core/ir/transform/signed_integer_polyfill.cc
index 6a64195..82865bd 100644
--- a/src/tint/lang/core/ir/transform/signed_integer_polyfill.cc
+++ b/src/tint/lang/core/ir/transform/signed_integer_polyfill.cc
@@ -150,7 +150,6 @@
                     core::ir::Capability::kAllow8BitIntegers,
                     core::ir::Capability::kAllow16BitIntegers,
                     core::ir::Capability::kAllow64BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before ir.SignedIntegerPolyfill");
 
diff --git a/src/tint/lang/core/ir/transform/std140.h b/src/tint/lang/core/ir/transform/std140.h
index cdd484d..b88cbe1 100644
--- a/src/tint/lang/core/ir/transform/std140.h
+++ b/src/tint/lang/core/ir/transform/std140.h
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kStd140Capabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
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 d082107..529711c 100644
--- a/src/tint/lang/core/ir/transform/value_to_let.h
+++ b/src/tint/lang/core/ir/transform/value_to_let.h
@@ -41,10 +41,10 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kValueToLetCapabilities{
-    core::ir::Capability::kAllow8BitIntegers,  core::ir::Capability::kAllow16BitIntegers,
+    core::ir::Capability::kAllow8BitIntegers,
+    core::ir::Capability::kAllow16BitIntegers,
     core::ir::Capability::kAllow64BitIntegers,
-
-    core::ir::Capability::kAllowNonCoreTypes,  core::ir::Capability::kLoosenValidationForShaderIO,
+    core::ir::Capability::kLoosenValidationForShaderIO,
 };
 
 /// 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 03e91e7..448e61d 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
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kVectorizeScalarMatrixConstructorsCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.h b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.h
index a794c49..9066c0f 100644
--- a/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.h
+++ b/src/tint/lang/core/ir/transform/zero_init_workgroup_memory.h
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const Capabilities kZeroInitWorkgroupMemoryCapabilities{
-    Capability::kAllowNonCoreTypes,
     Capability::kAllow8BitIntegers,
     Capability::kAllow16BitIntegers,
 };
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index d066dfb..8f5b0c9 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -178,6 +178,11 @@
     return false;
 }
 
+/// @returns true if @p type is in the core namespace
+bool IsCoreType(const core::type::Type* type) {
+    return std::string_view(type->TypeInfo().name).starts_with("tint::core");
+}
+
 /// @returns true if @p ty meets the basic function parameter rules (i.e. one of constructible,
 ///          pointer, handle).
 ///
@@ -2149,9 +2154,8 @@
         return;
     }
 
-    if (!capabilities_.Contains(Capability::kAllowNonCoreTypes)) {
-        // Check for core types, which are the only types declared in the `tint::core` namespace.
-        if (!std::string_view(root->TypeInfo().name).starts_with("tint::core")) {
+    if (!mod_.properties.Contains(Property::kAllowNonCoreTypes)) {
+        if (!IsCoreType(root)) {
             diag() << "non-core types not allowed in core IR";
             return;
         }
@@ -2489,7 +2493,7 @@
                     return false;
                 }
 
-                if (!capabilities_.Contains(Capability::kAllowNonCoreTypes)) {
+                if (!mod_.properties.Contains(Property::kAllowNonCoreTypes)) {
                     if (!t->ElemType()->Is<core::type::SampledTexture>()) {
                         diag() << "binding_array element type must be a sampled texture type";
                         return false;
diff --git a/src/tint/lang/core/ir/validator.h b/src/tint/lang/core/ir/validator.h
index 401a84e..b35ff4b 100644
--- a/src/tint/lang/core/ir/validator.h
+++ b/src/tint/lang/core/ir/validator.h
@@ -45,8 +45,6 @@
     kAllow64BitIntegers,
     /// Allows module scope `var`s to exist without an IO annotation
     kAllowUnannotatedModuleIOVariables,
-    /// Allows non-core types in the IR module
-    kAllowNonCoreTypes,
     /// Allows ShaderIO specific features, like blend_src on non-struct members.
     /// These are not separate capabilities, because they are enabled/disabled in lockstep with each
     /// other.
diff --git a/src/tint/lang/core/ir/validator_function_test.cc b/src/tint/lang/core/ir/validator_function_test.cc
index 0f142fe..d0e4a3a 100644
--- a/src/tint/lang/core/ir/validator_function_test.cc
+++ b/src/tint/lang/core/ir/validator_function_test.cc
@@ -2134,7 +2134,7 @@
 
     b.Append(f->Block(), [&] { b.Return(f); });
 
-    auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes});
+    auto res = ir::Validate(mod);
     EXPECT_EQ(res, Success);
 }
 
diff --git a/src/tint/lang/core/ir/validator_type_test.cc b/src/tint/lang/core/ir/validator_type_test.cc
index 4311e82..46449fe 100644
--- a/src/tint/lang/core/ir/validator_type_test.cc
+++ b/src/tint/lang/core/ir/validator_type_test.cc
@@ -50,7 +50,8 @@
 /// A mock non-core type used for testing the non-core type validation rule.
 class NonCoreType final : public Castable<NonCoreType, core::type::Type> {
   public:
-    explicit NonCoreType(uint32_t align = 0u) : Base(0u, core::type::Flags{}), align_(align) {}
+    explicit NonCoreType(uint32_t align, bool is_handle = false)
+        : Base(0u, core::type::Flags{}), align_(align), is_handle_(is_handle) {}
     bool Equals(const UniqueNode& other) const override {
         if (auto* other_nc = other.As<NonCoreType>()) {
             return other_nc->align_ == align_;
@@ -59,12 +60,14 @@
     }
     std::string FriendlyName() const override { return "NonCoreType"; }
     uint32_t Align() const override { return align_ == 0u ? Base::Align() : align_; }
+    bool IsHandle() const override { return is_handle_; }
     core::type::Type* Clone(core::type::CloneContext& ctx) const override {
         return ctx.dst.mgr->Get<NonCoreType>(align_);
     }
 
   private:
     uint32_t align_;
+    bool is_handle_;
 };
 }  // namespace tint::mock
 
@@ -329,16 +332,17 @@
 TEST_F(IR_ValidatorTest, StructMember_TypeAlignZero) {
     core::IOAttributes attrs = {};
     tint::Vector<const core::type::StructMember*, 4> members;
-    members.Push(ty.Get<core::type::StructMember>(mod.symbols.New("v"),
-                                                  ty.Get<tint::mock::NonCoreType>(), 0u, 0u,
-                                                  /* align */ 4u, 4u, std::move(attrs)));
+    members.Push(ty.Get<core::type::StructMember>(
+        mod.symbols.New("v"), ty.Get<tint::mock::NonCoreType>(/* align */ 0u), 0u, 0u,
+        /* align */ 4u, 4u, std::move(attrs)));
     auto* str_ty = ty.Get<core::type::Struct>(mod.symbols.New("MyStruct"), std::move(members),
                                               tint::RoundUp(0u, 16u));
 
     auto* v = b.Var(ty.ptr(private_, str_ty));
     mod.root_block->Append(v);
 
-    auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes});
+    mod.properties.Add(Property::kAllowNonCoreTypes);
+    auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_THAT(
         res.Failure().reason,
@@ -360,7 +364,8 @@
     auto* v = b.Var(ty.ptr(private_, str_ty));
     mod.root_block->Append(v);
 
-    auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes});
+    mod.properties.Add(Property::kAllowNonCoreTypes);
+    auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_THAT(
         res.Failure().reason,
@@ -714,7 +719,7 @@
 
 TEST_F(IR_ValidatorTest, NonCoreType) {
     auto* fn = b.Function("my_func", ty.void_());
-    fn->AppendParam(b.FunctionParam(ty.Get<tint::mock::NonCoreType>()));
+    fn->AppendParam(b.FunctionParam(ty.Get<tint::mock::NonCoreType>(/* align*/ 4u)));
     b.Append(fn->Block(), [&] {  //
         b.Return(fn);
     });
@@ -1056,13 +1061,17 @@
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^)"));
 }
 
-TEST_F(IR_ValidatorTest, BindingArray_AllowedNonSampledTextureWithCapability) {
+TEST_F(IR_ValidatorTest, BindingArray_AllowedNonSampledTextureWithNonCoreType) {
     b.Append(mod.root_block, [&] {
-        auto* var = b.Var("m", AddressSpace::kHandle, ty.binding_array(ty.external_texture(), 5));
+        auto* var =
+            b.Var("m", AddressSpace::kHandle,
+                  ty.binding_array(
+                      ty.Get<tint::mock::NonCoreType>(/*align */ 4u, /*is_handle*/ true), 5));
         var->SetBindingPoint(0, 0);
     });
 
-    auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes});
+    mod.properties.Add(Property::kAllowNonCoreTypes);
+    auto res = ir::Validate(mod);
     ASSERT_EQ(res, Success) << res.Failure();
 }
 
@@ -1097,7 +1106,7 @@
         b.Append(mod.root_block, [&] { b.Var("m", addr, ty.binding_array(ty.u32(), 4)); });
     }
 
-    auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes});
+    auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     if (addr == AddressSpace::kFunction) {
         EXPECT_THAT(res.Failure().reason,
@@ -1113,23 +1122,6 @@
     }
 }
 
-TEST_P(Type_BindingArrayInvalidAddressSpace, AllowedWithCapability) {
-    auto addr = GetParam();
-    if (addr == AddressSpace::kFunction) {
-        auto* fn = b.Function("my_func", ty.void_());
-        b.Append(fn->Block(), [&] {
-            b.Var("m", addr, ty.binding_array(ty.u32(), 4));
-            b.Return(fn);
-        });
-    } else {
-        b.Append(mod.root_block, [&] { b.Var("m", addr, ty.binding_array(ty.u32(), 4)); });
-    }
-
-    mod.properties.Add(Property::kAllowMslEntryPointInterface);
-    auto res = ir::Validate(mod, Capabilities{Capability::kAllowNonCoreTypes});
-    ASSERT_EQ(res, Success) << res.Failure();
-}
-
 // Note: kUniform, kStorage, and kImmediate are not tested here because they will still trigger a
 // different check
 INSTANTIATE_TEST_SUITE_P(IR_ValidatorTest,
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 41e6826..f071464 100644
--- a/src/tint/lang/hlsl/ir/member_builtin_call_test.cc
+++ b/src/tint/lang/hlsl/ir/member_builtin_call_test.cc
@@ -46,7 +46,13 @@
 namespace tint::hlsl::ir {
 namespace {
 
-using IR_HlslMemberBuiltinCallTest = core::ir::IRTestHelper;
+class IR_HlslMemberBuiltinCallTest : public core::ir::IRTestHelper {
+  protected:
+    void SetUp() override {
+        core::ir::IRTestHelper::SetUp();
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+    }
+};
 
 TEST_F(IR_HlslMemberBuiltinCallTest, Clone) {
     auto* buf = ty.Get<hlsl::type::ByteAddressBuffer>(core::Access::kReadWrite);
@@ -97,9 +103,7 @@
         b.Return(func, builtin);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
@@ -139,9 +143,7 @@
         b.Return(func, builtin);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
@@ -189,9 +191,7 @@
         b.Return(func, builtin);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
@@ -242,9 +242,7 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_EQ(res, Success);
 }
 
@@ -262,9 +260,7 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason,
               R"(:7:16 error: Load: expected exactly 1 results, got 0
@@ -301,9 +297,7 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason,
               R"(:7:17 error: Load: no matching call to 'Load(hlsl.byte_address_buffer<read>)'
@@ -402,9 +396,7 @@
         b.Return(func);
     });
 
-    auto res = core::ir::Validate(mod, core::ir::Capabilities{
-                                           core::ir::Capability::kAllowNonCoreTypes,
-                                       });
+    auto res = core::ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason,
diff --git a/src/tint/lang/hlsl/writer/printer/printer.h b/src/tint/lang/hlsl/writer/printer/printer.h
index e7fe62b..923a830 100644
--- a/src/tint/lang/hlsl/writer/printer/printer.h
+++ b/src/tint/lang/hlsl/writer/printer/printer.h
@@ -44,7 +44,6 @@
 const core::ir::Capabilities kPrinterCapabilities{
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// @param module the Tint IR module to generate
diff --git a/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate.h b/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate.h
index b62f8cc..2cbb716 100644
--- a/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate.h
+++ b/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate.h
@@ -48,7 +48,6 @@
 const core::ir::Capabilities kArrayOffsetFromImmediateCapabilities{
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// ArrayOffsetFromImmediates is a transform that adds an offset to storage buffer loads and stores
diff --git a/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate_test.cc b/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate_test.cc
index bb38fd0..b8a54d9 100644
--- a/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/array_offset_from_immediate_test.cc
@@ -43,7 +43,10 @@
 using namespace tint::core::number_suffixes;  // NOLINT
 
 struct IR_ArrayOffsetFromImmediateTest : core::ir::transform::TransformTest {
-    IR_ArrayOffsetFromImmediateTest() { capabilities = kArrayOffsetFromImmediateCapabilities; }
+    IR_ArrayOffsetFromImmediateTest() {
+        capabilities = kArrayOffsetFromImmediateCapabilities;
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+    }
 };
 
 // Test that offset is added to byte_address_buffer.Load
diff --git a/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform.h b/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform.h
index ec32132..f4988f6 100644
--- a/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform.h
+++ b/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform.h
@@ -45,7 +45,6 @@
 const core::ir::Capabilities kArrayOffsetFromUniformCapabilities{
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// ArrayOffsetFromUniform is a transform that adds an offset to storage buffer loads and stores
diff --git a/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform_test.cc b/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform_test.cc
index e669bed..e971045 100644
--- a/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/array_offset_from_uniform_test.cc
@@ -40,7 +40,10 @@
 using namespace tint::core::number_suffixes;  // NOLINT
 
 struct IR_ArrayOffsetFromUniformTest : core::ir::transform::TransformTest {
-    IR_ArrayOffsetFromUniformTest() { capabilities = kArrayOffsetFromUniformCapabilities; }
+    IR_ArrayOffsetFromUniformTest() {
+        capabilities = kArrayOffsetFromUniformCapabilities;
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+    }
 };
 
 // Test that offset is added to byte_address_buffer.Load
diff --git a/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc b/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc
index dc678c9..502d369 100644
--- a/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/binary_polyfill.cc
@@ -141,7 +141,6 @@
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
                     core::ir::Capability::kAllow16BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before hlsl.BinaryPolyfill");
 
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
index cee4043..111f402 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
@@ -2151,12 +2151,12 @@
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
                     core::ir::Capability::kAllow16BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before hlsl.BuiltinPolyfill");
 
     State{ir, config}.Process();
 
+    ir.properties.Add(core::ir::Property::kAllowNonCoreTypes);
     ir.properties.Add(core::ir::Property::kAllowVectorElementPointer);
 
     return Success;
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 0d803f7..d5a6c3b 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
@@ -6643,10 +6643,6 @@
 }
 
 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.vec4i(), 2_i));
@@ -6685,10 +6681,6 @@
 }
 
 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);
@@ -6727,10 +6719,6 @@
 }
 
 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.vec4u(), 2_u));
@@ -6769,10 +6757,6 @@
 }
 
 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);
@@ -6886,10 +6870,6 @@
 }
 
 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.vec4i(), 2_i));
@@ -7883,7 +7863,6 @@
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, SubgroupMatrixScalarAdd_I8) {
     capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
         core::ir::Capability::kAllow8BitIntegers,
     };
 
@@ -8177,8 +8156,6 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, SubgroupMatrixLoad_Workgroup) {
-    capabilities.Add(core::ir::Capability::kAllowNonCoreTypes);
-
     auto* mat_ty = ty.subgroup_matrix_left(ty.f32(), 4, 4);
     auto* wg_var = b.Var("wg", workgroup, ty.array<f32, 256>(), core::Access::kReadWrite);
     b.ir.root_block->Append(wg_var);
@@ -8221,8 +8198,6 @@
 }
 
 TEST_F(HlslWriter_BuiltinPolyfillTest, SubgroupMatrixStore_Workgroup) {
-    capabilities.Add(core::ir::Capability::kAllowNonCoreTypes);
-
     auto* mat_ty = ty.subgroup_matrix_left(ty.f32(), 4, 4);
     auto* wg_var = b.Var("wg", workgroup, ty.array<f32, 256>(), core::Access::kReadWrite);
     b.ir.root_block->Append(wg_var);
diff --git a/src/tint/lang/hlsl/writer/raise/decompose_storage_access.cc b/src/tint/lang/hlsl/writer/raise/decompose_storage_access.cc
index 27e783c..2df1fb3 100644
--- a/src/tint/lang/hlsl/writer/raise/decompose_storage_access.cc
+++ b/src/tint/lang/hlsl/writer/raise/decompose_storage_access.cc
@@ -1043,6 +1043,8 @@
 
     State{ir}.Process();
 
+    ir.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+
     return Success;
 }
 
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 7f3d96c..f3517ca 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
@@ -44,10 +44,7 @@
 namespace tint::hlsl::writer::raise {
 namespace {
 
-class HlslWriterDecomposeStorageAccessTest : public core::ir::transform::TransformTest {
-  public:
-    void SetUp() override { capabilities.Add(core::ir::Capability::kAllowNonCoreTypes); }
-};
+using HlslWriterDecomposeStorageAccessTest = core::ir::transform::TransformTest;
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, NoBufferAccess) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
@@ -1693,10 +1690,6 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageSubgroupMatrixLoad) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* var = b.Var("v", storage, ty.array<f32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1746,10 +1739,6 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageSubgroupMatrixLoadColMajor) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* var = b.Var("v", storage, ty.array<f32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1799,10 +1788,6 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageSubgroupMatrixLoadDynamicStride) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* var = b.Var("v", storage, ty.array<f32>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1856,10 +1841,6 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageSubgroupMatrixStore) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* var = b.Var("v", storage, ty.array<f32, 100>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1909,10 +1890,6 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageSubgroupMatrixStoreColMajor) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* var = b.Var("v", storage, ty.array<f32, 100>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -1962,10 +1939,6 @@
 }
 
 TEST_F(HlslWriterDecomposeStorageAccessTest, StorageSubgroupMatrixStoreDynamicStride) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* var = b.Var("v", storage, ty.array<f32, 100>(), core::Access::kReadWrite);
     var->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var);
@@ -2458,10 +2431,8 @@
     out << data.interlock;
     return out;
 }
-class DecomposeBuiltinAtomic : public core::ir::transform::TransformTestWithParam<AtomicData> {
-  public:
-    void SetUp() override { capabilities.Add(core::ir::Capability::kAllowNonCoreTypes); }
-};
+using DecomposeBuiltinAtomic = core::ir::transform::TransformTestWithParam<AtomicData>;
+
 TEST_P(DecomposeBuiltinAtomic, IndirectAccess) {
     auto params = GetParam();
 
diff --git a/src/tint/lang/hlsl/writer/raise/extract_ternary_values.h b/src/tint/lang/hlsl/writer/raise/extract_ternary_values.h
index 4a130c6..d733b50 100644
--- a/src/tint/lang/hlsl/writer/raise/extract_ternary_values.h
+++ b/src/tint/lang/hlsl/writer/raise/extract_ternary_values.h
@@ -42,7 +42,6 @@
 const core::ir::Capabilities kExtractTernaryValuesCapabilities{
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// ExtractTernaryValues is a transform used to extract the true and false values of an HLSL ternary
diff --git a/src/tint/lang/hlsl/writer/raise/pixel_local.cc b/src/tint/lang/hlsl/writer/raise/pixel_local.cc
index 2ec01d0..9ddf46d 100644
--- a/src/tint/lang/hlsl/writer/raise/pixel_local.cc
+++ b/src/tint/lang/hlsl/writer/raise/pixel_local.cc
@@ -245,12 +245,13 @@
     core::ir::AssertValid(ir,
                           core::ir::Capabilities{
                               core::ir::Capability::kAllow16BitIntegers,
-                              core::ir::Capability::kAllowNonCoreTypes,
                           },
                           "before hlsl.PixelLocal");
 
     State{config.options, ir}.Process();
 
+    ir.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+
     return Success;
 }
 
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 9402583..becebb0 100644
--- a/src/tint/lang/hlsl/writer/raise/pixel_local_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/pixel_local_test.cc
@@ -148,10 +148,6 @@
 }
 
 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);
@@ -233,10 +229,6 @@
 }
 
 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(), [&] {
@@ -335,10 +327,6 @@
 }
 
 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(), [&] {
@@ -439,10 +427,6 @@
 }
 
 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);
@@ -528,10 +512,6 @@
 }
 
 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);
@@ -639,10 +619,6 @@
 }
 
 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 ba7d93a..dbc2fa1 100644
--- a/src/tint/lang/hlsl/writer/raise/promote_initializers.h
+++ b/src/tint/lang/hlsl/writer/raise/promote_initializers.h
@@ -42,7 +42,6 @@
 const core::ir::Capabilities kPromoteInitializersCapabilities{
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow16BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// PromoteInitializers is a transform that moves inline struct and array initializers to a `let`
diff --git a/src/tint/lang/hlsl/writer/raise/replace_subgroup_matrix_init.cc b/src/tint/lang/hlsl/writer/raise/replace_subgroup_matrix_init.cc
index 9440367..9be409e 100644
--- a/src/tint/lang/hlsl/writer/raise/replace_subgroup_matrix_init.cc
+++ b/src/tint/lang/hlsl/writer/raise/replace_subgroup_matrix_init.cc
@@ -176,7 +176,6 @@
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
                     core::ir::Capability::kAllow16BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before hlsl.ReplaceSubgroupMatrixInit");
 
diff --git a/src/tint/lang/msl/writer/printer/printer.h b/src/tint/lang/msl/writer/printer/printer.h
index 12f1de9..35047ba 100644
--- a/src/tint/lang/msl/writer/printer/printer.h
+++ b/src/tint/lang/msl/writer/printer/printer.h
@@ -44,7 +44,6 @@
 const core::ir::Capabilities kPrinterCapabilities{
     core::ir::Capability::kAllow8BitIntegers,   //
     core::ir::Capability::kAllow64BitIntegers,  //
-    core::ir::Capability::kAllowNonCoreTypes,   //
 };
 
 /// @param module the Tint IR module to generate
diff --git a/src/tint/lang/msl/writer/raise/binary_polyfill.cc b/src/tint/lang/msl/writer/raise/binary_polyfill.cc
index 490ef98..acddd20 100644
--- a/src/tint/lang/msl/writer/raise/binary_polyfill.cc
+++ b/src/tint/lang/msl/writer/raise/binary_polyfill.cc
@@ -106,7 +106,6 @@
     AssertValid(ir,
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before msl.BinaryPolyfill");
 
diff --git a/src/tint/lang/msl/writer/raise/builtin_polyfill.cc b/src/tint/lang/msl/writer/raise/builtin_polyfill.cc
index de91237..6e568ac 100644
--- a/src/tint/lang/msl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/msl/writer/raise/builtin_polyfill.cc
@@ -1322,12 +1322,13 @@
     AssertValid(ir,
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before msl.BuiltinPolyfill");
 
     State{ir, config}.Process();
 
+    ir.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+
     return Success;
 }
 
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 8c38327..9d5dce2 100644
--- a/src/tint/lang/msl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/msl/writer/raise/builtin_polyfill_test.cc
@@ -2349,10 +2349,6 @@
 }
 
 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.vec2f());
@@ -2391,10 +2387,6 @@
 }
 
 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());
@@ -2473,10 +2465,6 @@
 }
 
 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.vec2f());
@@ -2516,10 +2504,6 @@
 }
 
 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.vec2f());
@@ -2560,10 +2544,6 @@
 }
 
 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.vec2f());
@@ -2643,10 +2623,6 @@
 }
 
 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());
@@ -2690,10 +2666,6 @@
 }
 
 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.vec3f());
@@ -2734,10 +2706,6 @@
 }
 
 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());
@@ -2779,10 +2747,6 @@
 }
 
 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.vec2f());
@@ -2861,10 +2825,6 @@
 }
 
 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.vec2f());
@@ -2903,10 +2863,6 @@
 }
 
 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/msl/writer/raise/convert_print_to_log_test.cc b/src/tint/lang/msl/writer/raise/convert_print_to_log_test.cc
index 378fed8..1f6013b 100644
--- a/src/tint/lang/msl/writer/raise/convert_print_to_log_test.cc
+++ b/src/tint/lang/msl/writer/raise/convert_print_to_log_test.cc
@@ -39,10 +39,7 @@
 namespace {
 
 class MslWriter_ConvertPrintToLogTest : public core::ir::transform::TransformTest {
-    void SetUp() override {
-        capabilities.Add(core::ir::Capability::kAllow8BitIntegers);
-        capabilities.Add(core::ir::Capability::kAllowNonCoreTypes);
-    }
+    void SetUp() override { capabilities.Add(core::ir::Capability::kAllow8BitIntegers); }
 };
 
 TEST_F(MslWriter_ConvertPrintToLogTest, NoPrint) {
diff --git a/src/tint/lang/msl/writer/raise/fix_type_layout.cc b/src/tint/lang/msl/writer/raise/fix_type_layout.cc
index fa3a622..bad1bc3 100644
--- a/src/tint/lang/msl/writer/raise/fix_type_layout.cc
+++ b/src/tint/lang/msl/writer/raise/fix_type_layout.cc
@@ -761,7 +761,6 @@
     AssertValid(ir,
                 tint::core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before msl.FixTypeLayout");
 
diff --git a/src/tint/lang/msl/writer/raise/module_constant.h b/src/tint/lang/msl/writer/raise/module_constant.h
index d7f57ab..2aeb524 100644
--- a/src/tint/lang/msl/writer/raise/module_constant.h
+++ b/src/tint/lang/msl/writer/raise/module_constant.h
@@ -42,7 +42,6 @@
 const core::ir::Capabilities kModuleConstantCapabilities{
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kAllow64BitIntegers,
-    core::ir::Capability::kAllowNonCoreTypes,
 };
 
 /// The set of polyfills that should be applied.
diff --git a/src/tint/lang/msl/writer/raise/module_scope_vars.cc b/src/tint/lang/msl/writer/raise/module_scope_vars.cc
index 617d3f6..2f61135 100644
--- a/src/tint/lang/msl/writer/raise/module_scope_vars.cc
+++ b/src/tint/lang/msl/writer/raise/module_scope_vars.cc
@@ -344,7 +344,6 @@
     AssertValid(ir,
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before msl.ModuleScopeVars");
 
diff --git a/src/tint/lang/msl/writer/raise/shader_io.cc b/src/tint/lang/msl/writer/raise/shader_io.cc
index c40e807..062c15d 100644
--- a/src/tint/lang/msl/writer/raise/shader_io.cc
+++ b/src/tint/lang/msl/writer/raise/shader_io.cc
@@ -344,7 +344,6 @@
     AssertValid(ir,
                 tint::core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before msl.ShaderIO");
 
diff --git a/src/tint/lang/msl/writer/raise/simd_ballot.cc b/src/tint/lang/msl/writer/raise/simd_ballot.cc
index bb20502..9a226b8 100644
--- a/src/tint/lang/msl/writer/raise/simd_ballot.cc
+++ b/src/tint/lang/msl/writer/raise/simd_ballot.cc
@@ -159,7 +159,6 @@
     AssertValid(ir,
                 tint::core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before msl.SimdBallot");
 
diff --git a/src/tint/lang/spirv/reader/lower/atomics.cc b/src/tint/lang/spirv/reader/lower/atomics.cc
index 4d288db..a2620a4 100644
--- a/src/tint/lang/spirv/reader/lower/atomics.cc
+++ b/src/tint/lang/spirv/reader/lower/atomics.cc
@@ -554,11 +554,7 @@
 }  // namespace
 
 Result<SuccessType> Atomics(core::ir::Module& ir) {
-    AssertValid(ir,
-                core::ir::Capabilities{
-                    core::ir::Capability::kAllowNonCoreTypes,
-                },
-                "before spirv.Atomics");
+    AssertValid(ir, "before spirv.Atomics");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/reader/lower/builtins.cc b/src/tint/lang/spirv/reader/lower/builtins.cc
index dcceed0..e625542 100644
--- a/src/tint/lang/spirv/reader/lower/builtins.cc
+++ b/src/tint/lang/spirv/reader/lower/builtins.cc
@@ -1309,11 +1309,7 @@
 }  // namespace
 
 Result<SuccessType> Builtins(core::ir::Module& ir) {
-    AssertValid(ir,
-                core::ir::Capabilities{
-                    core::ir::Capability::kAllowNonCoreTypes,
-                },
-                "before spirv.Builtins");
+    AssertValid(ir, "before spirv.Builtins");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/reader/lower/decompose_strided_array.cc b/src/tint/lang/spirv/reader/lower/decompose_strided_array.cc
index 104f717..dbaa5b9 100644
--- a/src/tint/lang/spirv/reader/lower/decompose_strided_array.cc
+++ b/src/tint/lang/spirv/reader/lower/decompose_strided_array.cc
@@ -251,11 +251,7 @@
 }  // namespace
 
 Result<SuccessType> DecomposeStridedArray(core::ir::Module& ir) {
-    core::ir::AssertValid(ir,
-                          core::ir::Capabilities{
-                              core::ir::Capability::kAllowNonCoreTypes,
-                          },
-                          "before spirv.DecomposeStridedArray");
+    core::ir::AssertValid(ir, "before spirv.DecomposeStridedArray");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/reader/lower/decompose_strided_array_test.cc b/src/tint/lang/spirv/reader/lower/decompose_strided_array_test.cc
index c4597d2..e0883f0 100644
--- a/src/tint/lang/spirv/reader/lower/decompose_strided_array_test.cc
+++ b/src/tint/lang/spirv/reader/lower/decompose_strided_array_test.cc
@@ -38,6 +38,11 @@
 
 class SpirvReader_DecomposeStridedArrayTest : public core::ir::transform::TransformTest {
   protected:
+    void SetUp() override {
+        core::ir::transform::TransformTest::SetUp();
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+    }
+
     const spirv::type::ExplicitLayoutArray* Array(const core::type::Type* elem_ty,
                                                   uint32_t count,
                                                   uint32_t stride) {
diff --git a/src/tint/lang/spirv/reader/lower/decompose_strided_matrix.cc b/src/tint/lang/spirv/reader/lower/decompose_strided_matrix.cc
index 2fa4b55..3cf9a68 100644
--- a/src/tint/lang/spirv/reader/lower/decompose_strided_matrix.cc
+++ b/src/tint/lang/spirv/reader/lower/decompose_strided_matrix.cc
@@ -362,11 +362,7 @@
 }  // namespace
 
 Result<SuccessType> DecomposeStridedMatrix(core::ir::Module& ir) {
-    AssertValid(ir,
-                core::ir::Capabilities{
-                    core::ir::Capability::kAllowNonCoreTypes,
-                },
-                "before spirv.DecomposeStridedMatrix");
+    AssertValid(ir, "before spirv.DecomposeStridedMatrix");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/reader/lower/decompose_strided_matrix_test.cc b/src/tint/lang/spirv/reader/lower/decompose_strided_matrix_test.cc
index 92e9560..31534cf 100644
--- a/src/tint/lang/spirv/reader/lower/decompose_strided_matrix_test.cc
+++ b/src/tint/lang/spirv/reader/lower/decompose_strided_matrix_test.cc
@@ -40,7 +40,7 @@
   protected:
     void SetUp() override {
         core::ir::transform::TransformTest::SetUp();
-        capabilities.Add(core::ir::Capability::kAllowNonCoreTypes);
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
         mod.properties.Add(core::ir::Property::kAllowStructMatrixDecorations);
     }
 
diff --git a/src/tint/lang/spirv/reader/lower/lower.cc b/src/tint/lang/spirv/reader/lower/lower.cc
index 153e6f5..a3e2131 100644
--- a/src/tint/lang/spirv/reader/lower/lower.cc
+++ b/src/tint/lang/spirv/reader/lower/lower.cc
@@ -65,6 +65,9 @@
     // `||` statements.
     TINT_CHECK_RESULT(core::ir::transform::RemoveTerminatorArgs(mod));
 
+    // All SPIR-V types should have been removed by the transforms above.
+    mod.properties.Remove(core::ir::Property::kAllowNonCoreTypes);
+
     core::ir::AssertValid(mod, "after spirv.Lower");
 
     return Success;
diff --git a/src/tint/lang/spirv/reader/lower/shader_io.cc b/src/tint/lang/spirv/reader/lower/shader_io.cc
index a5f6364..3933e4c 100644
--- a/src/tint/lang/spirv/reader/lower/shader_io.cc
+++ b/src/tint/lang/spirv/reader/lower/shader_io.cc
@@ -742,7 +742,6 @@
 Result<SuccessType> ShaderIO(core::ir::Module& ir) {
     AssertValid(ir,
                 core::ir::Capabilities{
-                    core::ir::Capability::kAllowNonCoreTypes,
                     core::ir::Capability::kLoosenValidationForShaderIO,
                 },
                 "before spirv.ShaderIO");
diff --git a/src/tint/lang/spirv/reader/lower/texture.cc b/src/tint/lang/spirv/reader/lower/texture.cc
index ec7a6ff..260a0da 100644
--- a/src/tint/lang/spirv/reader/lower/texture.cc
+++ b/src/tint/lang/spirv/reader/lower/texture.cc
@@ -1087,11 +1087,7 @@
 }  // namespace
 
 Result<SuccessType> Texture(core::ir::Module& ir) {
-    AssertValid(ir,
-                core::ir::Capabilities{
-                    core::ir::Capability::kAllowNonCoreTypes,
-                },
-                "before spirv.Texture");
+    AssertValid(ir, "before spirv.Texture");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/reader/lower/texture_test.cc b/src/tint/lang/spirv/reader/lower/texture_test.cc
index 8ababc8..b213253 100644
--- a/src/tint/lang/spirv/reader/lower/texture_test.cc
+++ b/src/tint/lang/spirv/reader/lower/texture_test.cc
@@ -42,6 +42,7 @@
   protected:
     void SetUp() override {
         core::ir::transform::TransformTest::SetUp();
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
         mod.properties.Add(core::ir::Property::kAllowPointerToHandle);
     }
 };
diff --git a/src/tint/lang/spirv/reader/lower/transpose_row_major.cc b/src/tint/lang/spirv/reader/lower/transpose_row_major.cc
index 690b632..3f33c48 100644
--- a/src/tint/lang/spirv/reader/lower/transpose_row_major.cc
+++ b/src/tint/lang/spirv/reader/lower/transpose_row_major.cc
@@ -756,11 +756,7 @@
 }  // namespace
 
 Result<SuccessType> TransposeRowMajor(core::ir::Module& ir) {
-    AssertValid(ir,
-                core::ir::Capabilities{
-                    core::ir::Capability::kAllowNonCoreTypes,
-                },
-                "before spirv.TransposeRowMajor");
+    AssertValid(ir, "before spirv.TransposeRowMajor");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/reader/lower/transpose_row_major_test.cc b/src/tint/lang/spirv/reader/lower/transpose_row_major_test.cc
index da9be74..1992765 100644
--- a/src/tint/lang/spirv/reader/lower/transpose_row_major_test.cc
+++ b/src/tint/lang/spirv/reader/lower/transpose_row_major_test.cc
@@ -39,7 +39,6 @@
   protected:
     void SetUp() override {
         core::ir::transform::TransformTest::SetUp();
-        capabilities.Add(core::ir::Capability::kAllowNonCoreTypes);
         mod.properties.Add(core::ir::Property::kAllowStructMatrixDecorations);
     }
 };
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 c9b42d7..e472c71 100644
--- a/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
+++ b/src/tint/lang/spirv/reader/lower/vector_element_pointer.cc
@@ -156,7 +156,6 @@
 Result<SuccessType> VectorElementPointer(core::ir::Module& ir) {
     core::ir::AssertValid(ir,
                           core::ir::Capabilities{
-                              core::ir::Capability::kAllowNonCoreTypes,
                               core::ir::Capability::kLoosenValidationForShaderIO,
                           },
                           "before spirv.VectorElementPointer");
diff --git a/src/tint/lang/spirv/reader/parser/helper_test.h b/src/tint/lang/spirv/reader/parser/helper_test.h
index 1662691..15fdb4c 100644
--- a/src/tint/lang/spirv/reader/parser/helper_test.h
+++ b/src/tint/lang/spirv/reader/parser/helper_test.h
@@ -70,12 +70,8 @@
         // Parse the SPIR-V to produce an IR module.
         TINT_CHECK_RESULT_UNWRAP(parsed, Parse(binary, options));
 
-        // Validate the IR module against the capabilities supported by the SPIR-V dialect.
-        core::ir::AssertValid(parsed,
-                              core::ir::Capabilities{
-                                  core::ir::Capability::kAllowNonCoreTypes,
-                              },
-                              "after spirv.test");
+        // Validate the IR module.
+        core::ir::AssertValid(parsed, "after spirv.test");
 
         // Return the disassembled IR module.
         return core::ir::Disassembler(parsed).Plain();
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index e8bc21a..34e4bbf 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -185,6 +185,7 @@
         // Set properties that are supported by the SPIR-V parser.
         ir_.properties.Add(core::ir::Property::kAllowLocationForNumericComposites);
         ir_.properties.Add(core::ir::Property::kAllowMultipleEntryPoints);
+        ir_.properties.Add(core::ir::Property::kAllowNonCoreTypes);
         ir_.properties.Add(core::ir::Property::kAllowOverrides);
         ir_.properties.Add(core::ir::Property::kAllowPhonyInstructions);
         ir_.properties.Add(core::ir::Property::kAllowPointSizeBuiltin);
diff --git a/src/tint/lang/spirv/writer/analysis/relaxed_precision_decorations_test.cc b/src/tint/lang/spirv/writer/analysis/relaxed_precision_decorations_test.cc
index 7454997..0b4c108 100644
--- a/src/tint/lang/spirv/writer/analysis/relaxed_precision_decorations_test.cc
+++ b/src/tint/lang/spirv/writer/analysis/relaxed_precision_decorations_test.cc
@@ -43,9 +43,10 @@
 
 class SpirvWriter_RelaxedPrecisionDecorationsTest : public core::ir::IRTestHelper {
   protected:
-    const core::ir::Capabilities kValidationCapabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
+    void SetUp() override {
+        core::ir::IRTestHelper::SetUp();
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+    }
 
     type::Image* MakeSampledImage() {
         auto dim = type::Dim::kD2;
@@ -157,7 +158,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision due to the f16 texel format.
     // The texel value will be relaxed precision since it is converted to f16.
@@ -205,7 +206,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision due to the f16 texel format.
     // The texel value and intermediate lets will be relaxed precision since they are converted to
@@ -248,7 +249,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision due to the f16 texel format.
     // The texel value will NOT be relaxed precision since it is used as an f32 value.
@@ -287,7 +288,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision due to the f16 texel format.
     // The texel value will be relaxed precision since it is converted from f16.
@@ -334,7 +335,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision since it is used with an f16 conversion.
     // The texel value and intermediate lets will be relaxed precision since they are converted from
@@ -374,7 +375,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision due to the f16 texel format.
     // The texel value will NOT be relaxed precision since it is not converted from f16.
@@ -415,7 +416,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision since all accesses are converted to/from f16.
     // The texel value will be relaxed precision since it is converted to f16.
@@ -456,7 +457,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // Neither the image variable nor the texel value get relaxed precision.
     auto decorations = GetRelaxedPrecisionDecorations(mod);
@@ -494,7 +495,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable and the texel value will be relaxed precision.
     auto decorations = GetRelaxedPrecisionDecorations(mod);
@@ -532,7 +533,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // Neither the image variable nor the texel value get relaxed precision.
     auto decorations = GetRelaxedPrecisionDecorations(mod);
@@ -583,7 +584,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The first texel value will be relaxed precision since it is converted to f16.
     // The image variable and second texel value will NOT be relaxed precision.
@@ -632,7 +633,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will NOT be relaxed precision since one write is not relaxed.
     // The first texel value will be relaxed precision since it is converted from f16.
@@ -686,7 +687,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision since all accesses are converted to/from f16.
     // The sampled value will be relaxed precision since it is converted to f16.
@@ -740,7 +741,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // Neither the image variable nor the texel value get relaxed precision.
     auto decorations = GetRelaxedPrecisionDecorations(mod);
@@ -779,7 +780,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision due to the f16 texel format.
     // The texel value will NOT be relaxed precision since it is converted to i32.
@@ -823,7 +824,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will NOT be relaxed precision because it is an integer format.
     // The texel value WILL be relaxed precision since it is converted to f16.
@@ -880,7 +881,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision since all accesses are converted to/from f16.
     // The texel value will be relaxed precision since it is converted to f16.
@@ -934,7 +935,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // Neither the image variable, function parameter, nor the texel value get relaxed precision.
     auto decorations = GetRelaxedPrecisionDecorations(mod);
@@ -1003,7 +1004,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable, both function parameters, and the texel value will be relaxed precision.
     auto decorations = GetRelaxedPrecisionDecorations(mod);
@@ -1068,7 +1069,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The first texel value will be relaxed precision since it is converted to f16.
     // The image variable, function parameter, and second texel value will NOT be relaxed precision.
@@ -1113,7 +1114,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision since all accesses are converted to f16.
     // The texel value and intermediate swizzle will be relaxed precision since they are converted
@@ -1159,7 +1160,7 @@
 }
 )";
     EXPECT_EQ(src, str());
-    EXPECT_EQ(Validate(mod, kValidationCapabilities), Success);
+    EXPECT_EQ(Validate(mod), Success);
 
     // The image variable will be relaxed precision since all accesses are converted from f16.
     // The texel value (which is swizzle result), and converted f32 value will be relaxed precision
diff --git a/src/tint/lang/spirv/writer/printer/printer.h b/src/tint/lang/spirv/writer/printer/printer.h
index 752ffe4..edf7023 100644
--- a/src/tint/lang/spirv/writer/printer/printer.h
+++ b/src/tint/lang/spirv/writer/printer/printer.h
@@ -42,7 +42,6 @@
 
 // The capabilities that might be needed due to raising.
 const core::ir::Capabilities kPrinterCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
index eaa1671..326b351 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -1499,12 +1499,13 @@
     AssertValid(ir,
                 core::ir::Capabilities{
                     core::ir::Capability::kAllow8BitIntegers,
-                    core::ir::Capability::kAllowNonCoreTypes,
                 },
                 "before spirv.BuiltinPolyfill");
 
     State{ir, config}.Process();
 
+    ir.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+
     return Success;
 }
 
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 6c487cc..6b406ed 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
@@ -51,7 +51,6 @@
     void SetUp() override {
         capabilities.Add(core::ir::Capability::kAllow8BitIntegers);
         capabilities.Add(core::ir::Capability::kAllow64BitIntegers);
-        capabilities.Add(core::ir::Capability::kAllowNonCoreTypes);
     }
 };
 
diff --git a/src/tint/lang/spirv/writer/raise/case_switch_to_if_else.cc b/src/tint/lang/spirv/writer/raise/case_switch_to_if_else.cc
index 6580c1b..9e0e19a 100644
--- a/src/tint/lang/spirv/writer/raise/case_switch_to_if_else.cc
+++ b/src/tint/lang/spirv/writer/raise/case_switch_to_if_else.cc
@@ -46,10 +46,6 @@
 
 namespace {
 
-const core::ir::Capabilities kCaseSwitchToIfElseCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
-};
-
 /// PIMPL state for the transform, for a single function.
 struct State {
     /// The IR module.
@@ -145,7 +141,7 @@
 }  // namespace
 
 Result<SuccessType> CaseSwitchToIfElse(core::ir::Module& ir) {
-    core::ir::AssertValid(ir, kCaseSwitchToIfElseCapabilities, "before spirv.CaseSwitchToIfElse");
+    core::ir::AssertValid(ir, "before spirv.CaseSwitchToIfElse");
 
     State{ir}.Process();
 
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 5d9272b..1fc2e1e 100644
--- a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.h
+++ b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.h
@@ -41,7 +41,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kExpandImplicitSplatsCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
 };
 
diff --git a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc
index 2ba9333..0cb1d77 100644
--- a/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc
+++ b/src/tint/lang/spirv/writer/raise/fork_explicit_layout_types.cc
@@ -374,6 +374,8 @@
 
     State{ir, version}.Process();
 
+    ir.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+
     return Success;
 }
 
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 5905dee..4ad6483 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
@@ -43,7 +43,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kForkExplicitLayoutTypesCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
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 6d41cfe..f97b9f2 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
@@ -41,7 +41,13 @@
 using namespace tint::core::fluent_types;     // NOLINT
 using namespace tint::core::number_suffixes;  // NOLINT
 
-using SpirvWriter_ForkExplicitLayoutTypesTest = core::ir::transform::TransformTest;
+class SpirvWriter_ForkExplicitLayoutTypesTest : public core::ir::transform::TransformTest {
+  protected:
+    void SetUp() override {
+        core::ir::transform::TransformTest::SetUp();
+        mod.properties.Add(core::ir::Property::kAllowNonCoreTypes);
+    }
+};
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, NoModify_Struct_NotInHostShareable) {
     auto* structure = ty.Struct(mod.symbols.New("MyStruct"), {
@@ -195,10 +201,6 @@
 // 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);
@@ -560,10 +562,6 @@
 }
 
 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));
@@ -1954,10 +1952,6 @@
 }
 
 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);
@@ -2032,10 +2026,6 @@
 }
 
 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);
@@ -2139,10 +2129,6 @@
 }
 
 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);
@@ -2220,10 +2206,6 @@
 }
 
 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);
@@ -2330,10 +2312,6 @@
 }
 
 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);
@@ -2445,10 +2423,6 @@
 }
 
 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);
@@ -2503,10 +2477,6 @@
 }
 
 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));
@@ -2620,10 +2590,6 @@
 }
 
 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));
@@ -2724,10 +2690,6 @@
 }
 
 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()},
@@ -2938,10 +2900,6 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Spv14_CopyLogical_ArrayRemoveLayout) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* array_ty = ty.array(ty.u32(), 4);
     auto* ssbo = b.Var("ssbo", ty.ptr(storage, array_ty));
     ssbo->SetBindingPoint(0, 0);
@@ -2995,10 +2953,6 @@
 }
 
 TEST_F(SpirvWriter_ForkExplicitLayoutTypesTest, Spv14_CopyLogical_ArrayAddLayout) {
-    capabilities = core::ir::Capabilities{
-        core::ir::Capability::kAllowNonCoreTypes,
-    };
-
     auto* array_ty = ty.array(ty.u32(), 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 8a22e90..b097382 100644
--- a/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h
+++ b/src/tint/lang/spirv/writer/raise/handle_matrix_arithmetic.h
@@ -40,7 +40,6 @@
 
 // The capabilities that the transform can support.
 const core::ir::Capabilities kHandleMatrixArithmeticCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
 };
 
diff --git a/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.cc b/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.cc
index ae197f1..ee7be6f 100644
--- a/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.cc
+++ b/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.cc
@@ -90,8 +90,7 @@
 }  // namespace
 
 Result<SuccessType> KeepBindingArrayAsPointer(core::ir::Module& ir) {
-    core::ir::AssertValid(ir, kKeepBindingArrayAsPointerCapabilities,
-                          "before spirv.KeepBindingArrayAsPointer");
+    core::ir::AssertValid(ir, "before spirv.KeepBindingArrayAsPointer");
 
     State{ir}.Process();
 
diff --git a/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.h b/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.h
index efc1b76..f47555c 100644
--- a/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.h
+++ b/src/tint/lang/spirv/writer/raise/keep_binding_array_as_pointer.h
@@ -38,11 +38,6 @@
 
 namespace tint::spirv::writer::raise {
 
-// The capabilities that the transform can support.
-const core::ir::Capabilities kKeepBindingArrayAsPointerCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
-};
-
 /// KeepBindingArrayAsPointer is a transform that ensures that binding_arrays are never stored by
 /// value but only used via a pointer to them. This is used to produce SPIR-V that's more similar to
 /// what drivers typically ingest where OpTypeArray<OpTypeImage> is always kept as a pointer.
diff --git a/src/tint/lang/spirv/writer/raise/merge_return.cc b/src/tint/lang/spirv/writer/raise/merge_return.cc
index 40bda49..3c549c8 100644
--- a/src/tint/lang/spirv/writer/raise/merge_return.cc
+++ b/src/tint/lang/spirv/writer/raise/merge_return.cc
@@ -42,7 +42,6 @@
 
 // The capabilities that the transform can support.
 const core::ir::Capabilities kMergeReturnCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
 };
 
diff --git a/src/tint/lang/spirv/writer/raise/pass_matrix_by_pointer.cc b/src/tint/lang/spirv/writer/raise/pass_matrix_by_pointer.cc
index 301ce67..aefd0e5 100644
--- a/src/tint/lang/spirv/writer/raise/pass_matrix_by_pointer.cc
+++ b/src/tint/lang/spirv/writer/raise/pass_matrix_by_pointer.cc
@@ -128,11 +128,7 @@
 }  // namespace
 
 Result<SuccessType> PassMatrixByPointer(core::ir::Module& ir) {
-    core::ir::AssertValid(ir,
-                          core::ir::Capabilities{
-                              core::ir::Capability::kAllowNonCoreTypes,
-                          },
-                          "before spirv.PassMatrixByPointer");
+    core::ir::AssertValid(ir, "before spirv.PassMatrixByPointer");
 
     State{ir}.Process();
 
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 99e3210..602a8ad 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
@@ -40,7 +40,6 @@
 
 // The capabilities that the transform can support.
 const core::ir::Capabilities kRemoveUnreachableInLoopContinuingCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
 };
 
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.h b/src/tint/lang/spirv/writer/raise/shader_io.h
index 3e0681c..27a00cb 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.h
+++ b/src/tint/lang/spirv/writer/raise/shader_io.h
@@ -44,7 +44,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kShaderIOCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
diff --git a/src/tint/lang/spirv/writer/raise/unary_polyfill.h b/src/tint/lang/spirv/writer/raise/unary_polyfill.h
index b853754..7852550 100644
--- a/src/tint/lang/spirv/writer/raise/unary_polyfill.h
+++ b/src/tint/lang/spirv/writer/raise/unary_polyfill.h
@@ -40,7 +40,6 @@
 
 // The capabilities that the transform can support.
 const core::ir::Capabilities kPolyfillUnaryCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
 };
 
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 098d633..620039e 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
@@ -40,7 +40,6 @@
 
 /// The capabilities that the transform can support.
 const core::ir::Capabilities kVarForDynamicIndexCapabilities{
-    core::ir::Capability::kAllowNonCoreTypes,
     core::ir::Capability::kAllow8BitIntegers,
     core::ir::Capability::kLoosenValidationForShaderIO,
 };
diff --git a/src/tint/lang/wgsl/writer/writer.cc b/src/tint/lang/wgsl/writer/writer.cc
index 3716326..9a8d5fa 100644
--- a/src/tint/lang/wgsl/writer/writer.cc
+++ b/src/tint/lang/wgsl/writer/writer.cc
@@ -29,6 +29,7 @@
 
 #include <memory>
 
+#include "src/tint/lang/core/ir/validator.h"
 #include "src/tint/lang/wgsl/program/program.h"
 #include "src/tint/lang/wgsl/writer/ast_printer/ast_printer.h"
 #include "src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.h"
@@ -56,6 +57,11 @@
 }
 
 Result<Program> ProgramFromIR(core::ir::Module& module, const Options& options) {
+    const core::ir::Properties kUnsupportedProperties{
+        core::ir::Property::kAllowNonCoreTypes,
+    };
+    AssertNoUnsupportedProperties(module, kUnsupportedProperties);
+
     // core-dialect -> WGSL-dialect
     TINT_CHECK_RESULT(Raise(module));