Move traits into utils.

This CL moves the tint/traits file into tint/utils/traits. Traits is one
of the few items not in utils which is referred to by utils.

Change-Id: Ie955398f24e949b7618fdc868dbcb903fe20b3f1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127400
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 5240dd2..f20948d 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -218,7 +218,6 @@
     "symbol.h",
     "symbol_table.cc",
     "symbol_table.h",
-    "traits.h",
     "utils/bitcast.h",
     "utils/bitset.h",
     "utils/block_allocator.h",
@@ -243,6 +242,7 @@
     "utils/string.h",
     "utils/string_stream.cc",
     "utils/string_stream.h",
+    "utils/traits.h",
     "utils/unique_allocator.h",
     "utils/unique_vector.h",
     "utils/vector.h",
@@ -1606,6 +1606,7 @@
       "utils/slice_test.cc",
       "utils/string_stream_test.cc",
       "utils/string_test.cc",
+      "utils/traits_test.cc",
       "utils/transform_test.cc",
       "utils/unique_allocator_test.cc",
       "utils/unique_vector_test.cc",
@@ -1990,7 +1991,6 @@
       "switch_test.cc",
       "symbol_table_test.cc",
       "symbol_test.cc",
-      "traits_test.cc",
     ]
     deps = [ ":libtint_base_src" ]
   }
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 7b595fd..820366a 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -348,7 +348,6 @@
   symbol.cc
   symbol.h
   tint.cc
-  traits.h
   transform/add_empty_entry_point.cc
   transform/add_empty_entry_point.h
   transform/add_block_attribute.cc
@@ -535,6 +534,7 @@
   utils/string.h
   utils/string_stream.cc
   utils/string_stream.h
+  utils/traits.h
   utils/unique_allocator.h
   utils/unique_vector.h
   utils/vector.h
@@ -969,7 +969,6 @@
     symbol_test.cc
     test_main.cc
     text/unicode_test.cc
-    traits_test.cc
     transform/transform_test.cc
     type/array_test.cc
     type/atomic_test.cc
@@ -1013,6 +1012,7 @@
     utils/slice_test.cc
     utils/string_stream_test.cc
     utils/string_test.cc
+    utils/traits_test.cc
     utils/transform_test.cc
     utils/unique_allocator_test.cc
     utils/unique_vector_test.cc
diff --git a/src/tint/ast/module.h b/src/tint/ast/module.h
index 948f894..0125fb3f 100644
--- a/src/tint/ast/module.h
+++ b/src/tint/ast/module.h
@@ -80,7 +80,7 @@
     auto& GlobalVariables() { return global_variables_; }
 
     /// @returns the global variable declarations of kind 'T' for the module
-    template <typename T, typename = traits::EnableIfIsType<T, Variable>>
+    template <typename T, typename = utils::traits::EnableIfIsType<T, Variable>>
     auto Globals() const {
         utils::Vector<const T*, 32> out;
         out.Reserve(global_variables_.Length());
diff --git a/src/tint/ast/test_helper.h b/src/tint/ast/test_helper.h
index e26c10a..6ef328d 100644
--- a/src/tint/ast/test_helper.h
+++ b/src/tint/ast/test_helper.h
@@ -96,7 +96,7 @@
         const auto* got_arg = got->arguments[arg_idx++];
 
         using T = std::decay_t<decltype(expected_arg)>;
-        if constexpr (traits::IsStringLike<T>) {
+        if constexpr (utils::traits::IsStringLike<T>) {
             ASSERT_TRUE(got_arg->Is<IdentifierExpression>());
             CheckIdentifier(got_arg->As<IdentifierExpression>()->identifier, expected_arg);
         } else if constexpr (IsTemplatedIdentifierMatcher<T>::value) {
diff --git a/src/tint/ast/traverse_expressions.h b/src/tint/ast/traverse_expressions.h
index 84a10c3..780de1a 100644
--- a/src/tint/ast/traverse_expressions.h
+++ b/src/tint/ast/traverse_expressions.h
@@ -62,8 +62,9 @@
 /// @return true on success, false on error
 template <TraverseOrder ORDER = TraverseOrder::LeftToRight, typename CALLBACK>
 bool TraverseExpressions(const Expression* root, diag::List& diags, CALLBACK&& callback) {
-    using EXPR_TYPE = std::remove_pointer_t<traits::ParameterType<CALLBACK, 0>>;
-    constexpr static bool kHasDepthArg = traits::SignatureOfT<CALLBACK>::parameter_count == 2;
+    using EXPR_TYPE = std::remove_pointer_t<utils::traits::ParameterType<CALLBACK, 0>>;
+    constexpr static bool kHasDepthArg =
+        utils::traits::SignatureOfT<CALLBACK>::parameter_count == 2;
 
     struct Pending {
         const Expression* expr;
diff --git a/src/tint/castable.h b/src/tint/castable.h
index 71e3ab8..33d61396 100644
--- a/src/tint/castable.h
+++ b/src/tint/castable.h
@@ -20,8 +20,8 @@
 #include <tuple>
 #include <utility>
 
-#include "src/tint/traits.h"
 #include "src/tint/utils/crc32.h"
+#include "src/tint/utils/traits.h"
 
 #if defined(__clang__)
 /// Temporarily disable certain warnings when using Castable API
@@ -60,7 +60,7 @@
 /// True if all template types that are not Ignore derive from CastableBase
 template <typename... TYPES>
 static constexpr bool IsCastable =
-    ((traits::IsTypeOrDerived<TYPES, CastableBase> || std::is_same_v<TYPES, Ignore>)&&...) &&
+    ((utils::traits::IsTypeOrDerived<TYPES, CastableBase> || std::is_same_v<TYPES, Ignore>)&&...) &&
     !(std::is_same_v<TYPES, Ignore> && ...);
 
 /// Helper macro to instantiate the TypeInfo<T> template for `CLASS`.
@@ -220,8 +220,8 @@
             return HashCodeOf<std::remove_cv_t<std::tuple_element_t<0, TUPLE>>>();
         } else {
             constexpr auto kMid = kCount / 2;
-            return CombinedHashCodeOfTuple<traits::SliceTuple<0, kMid, TUPLE>>() |
-                   CombinedHashCodeOfTuple<traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
+            return CombinedHashCodeOfTuple<utils::traits::SliceTuple<0, kMid, TUPLE>>() |
+                   CombinedHashCodeOfTuple<utils::traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
         }
     }
 
@@ -245,8 +245,8 @@
                 // Possibly one of the types in `TUPLE`.
                 // Split the search in two, and scan each block.
                 static constexpr auto kMid = kCount / 2;
-                return IsAnyOfTuple<traits::SliceTuple<0, kMid, TUPLE>>() ||
-                       IsAnyOfTuple<traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
+                return IsAnyOfTuple<utils::traits::SliceTuple<0, kMid, TUPLE>>() ||
+                       IsAnyOfTuple<utils::traits::SliceTuple<kMid, kCount - kMid, TUPLE>>();
             }
             return false;
         }
@@ -442,7 +442,7 @@
     /// object is of, or derives from the class `TO`.
     template <int FLAGS = 0, typename Pred = detail::Infer>
     inline bool Is(Pred&& pred) const {
-        using TO = typename std::remove_pointer<traits::ParameterType<Pred, 0>>::type;
+        using TO = typename std::remove_pointer<utils::traits::ParameterType<Pred, 0>>::type;
         return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this), std::forward<Pred>(pred));
     }
 
@@ -512,7 +512,7 @@
 template <typename A, typename B>
 struct CastableCommonBaseImpl<A, B> {
     /// The common base class for A, B and OTHERS
-    using type = std::conditional_t<traits::IsTypeOrDerived<A, B>,
+    using type = std::conditional_t<utils::traits::IsTypeOrDerived<A, B>,
                                     B,  // A derives from B
                                     CastableCommonBase<A, typename B::TrueBase>>;
 };
diff --git a/src/tint/clone_context.h b/src/tint/clone_context.h
index 8e0e2ff..9012982 100644
--- a/src/tint/clone_context.h
+++ b/src/tint/clone_context.h
@@ -25,10 +25,10 @@
 #include "src/tint/debug.h"
 #include "src/tint/program_id.h"
 #include "src/tint/symbol.h"
-#include "src/tint/traits.h"
 #include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/hashset.h"
+#include "src/tint/utils/traits.h"
 #include "src/tint/utils/vector.h"
 
 // Forward declarations
@@ -74,8 +74,8 @@
     /// ParamTypeIsPtrOf<F, T> is true iff the first parameter of
     /// F is a pointer of (or derives from) type T.
     template <typename F, typename T>
-    static constexpr bool ParamTypeIsPtrOf =
-        traits::IsTypeOrDerived<typename std::remove_pointer<traits::ParameterType<F, 0>>::type, T>;
+    static constexpr bool ParamTypeIsPtrOf = utils::traits::
+        IsTypeOrDerived<typename std::remove_pointer<utils::traits::ParameterType<F, 0>>::type, T>;
 
   public:
     /// SymbolTransform is a function that takes a symbol and returns a new
@@ -303,8 +303,9 @@
     ///        `T* (T*)`, where `T` derives from Cloneable
     /// @returns this CloneContext so calls can be chained
     template <typename F>
-    traits::EnableIf<ParamTypeIsPtrOf<F, Cloneable>, CloneContext>& ReplaceAll(F&& replacer) {
-        using TPtr = traits::ParameterType<F, 0>;
+    utils::traits::EnableIf<ParamTypeIsPtrOf<F, Cloneable>, CloneContext>& ReplaceAll(
+        F&& replacer) {
+        using TPtr = utils::traits::ParameterType<F, 0>;
         using T = typename std::remove_pointer<TPtr>::type;
         for (auto& transform : transforms_) {
             bool already_registered = transform.typeinfo->Is(&TypeInfo::Of<T>()) ||
@@ -355,7 +356,9 @@
     /// references of the original object. A type mismatch will result in an
     /// assertion in debug builds, and undefined behavior in release builds.
     /// @returns this CloneContext so calls can be chained
-    template <typename WHAT, typename WITH, typename = traits::EnableIfIsType<WITH, Cloneable>>
+    template <typename WHAT,
+              typename WITH,
+              typename = utils::traits::EnableIfIsType<WITH, Cloneable>>
     CloneContext& Replace(const WHAT* what, const WITH* with) {
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, src, what);
         TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Clone, dst, with);
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 5a39dbb..b090bdf 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -90,7 +90,8 @@
     /// @param args the arguments
     /// @returns the new constant value
     template <typename T, typename... ARGS>
-    traits::EnableIf<traits::IsTypeOrDerived<T, constant::Value>, const T>* create(ARGS&&... args) {
+    utils::traits::EnableIf<utils::traits::IsTypeOrDerived<T, constant::Value>, const T>* create(
+        ARGS&&... args) {
         return ir.constants.Create<T>(std::forward<ARGS>(args)...);
     }
 
diff --git a/src/tint/number.h b/src/tint/number.h
index 7bc1eea..06ab7fd 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -21,10 +21,10 @@
 #include <limits>
 #include <optional>
 
-#include "src/tint/traits.h"
 #include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/result.h"
 #include "src/tint/utils/string_stream.h"
+#include "src/tint/utils/traits.h"
 
 // Forward declaration
 namespace tint {
@@ -274,7 +274,7 @@
 /// However since C++ don't have native binary16 type, the value is stored as float.
 using f16 = Number<detail::NumberKindF16>;
 
-template <typename T, traits::EnableIf<IsFloatingPoint<T>>* = nullptr>
+template <typename T, utils::traits::EnableIf<IsFloatingPoint<T>>* = nullptr>
 inline const auto kPi = T(UnwrapNumber<T>(3.14159265358979323846));
 
 /// True iff T is an abstract number type
@@ -282,7 +282,7 @@
 constexpr bool IsAbstract = std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>;
 
 /// @returns the friendly name of Number type T
-template <typename T, traits::EnableIf<IsNumber<T>>* = nullptr>
+template <typename T, utils::traits::EnableIf<IsNumber<T>>* = nullptr>
 const char* FriendlyName() {
     if constexpr (std::is_same_v<T, AInt>) {
         return "abstract-int";
@@ -302,7 +302,7 @@
 }
 
 /// @returns the friendly name of T when T is bool
-template <typename T, traits::EnableIf<std::is_same_v<T, bool>>* = nullptr>
+template <typename T, utils::traits::EnableIf<std::is_same_v<T, bool>>* = nullptr>
 const char* FriendlyName() {
     return "bool";
 }
@@ -438,7 +438,8 @@
 }
 
 /// @returns a + b, or an empty optional if the resulting value overflowed the float value
-template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+template <typename FloatingPointT,
+          typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
 inline std::optional<FloatingPointT> CheckedAdd(FloatingPointT a, FloatingPointT b) {
     auto result = FloatingPointT{a.value + b.value};
     if (!std::isfinite(result.value)) {
@@ -470,7 +471,8 @@
 }
 
 /// @returns a + b, or an empty optional if the resulting value overflowed the float value
-template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+template <typename FloatingPointT,
+          typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
 inline std::optional<FloatingPointT> CheckedSub(FloatingPointT a, FloatingPointT b) {
     auto result = FloatingPointT{a.value - b.value};
     if (!std::isfinite(result.value)) {
@@ -514,7 +516,8 @@
 }
 
 /// @returns a * b, or an empty optional if the resulting value overflowed the float value
-template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+template <typename FloatingPointT,
+          typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
 inline std::optional<FloatingPointT> CheckedMul(FloatingPointT a, FloatingPointT b) {
     auto result = FloatingPointT{a.value * b.value};
     if (!std::isfinite(result.value)) {
@@ -537,7 +540,8 @@
 }
 
 /// @returns a / b, or an empty optional if the resulting value overflowed the float value
-template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+template <typename FloatingPointT,
+          typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
 inline std::optional<FloatingPointT> CheckedDiv(FloatingPointT a, FloatingPointT b) {
     if (b == FloatingPointT{0.0} || b == FloatingPointT{-0.0}) {
         return {};
@@ -577,7 +581,8 @@
 
 /// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the
 /// float value
-template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+template <typename FloatingPointT,
+          typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
 inline std::optional<FloatingPointT> CheckedMod(FloatingPointT a, FloatingPointT b) {
     if (b == FloatingPointT{0.0} || b == FloatingPointT{-0.0}) {
         return {};
@@ -599,7 +604,8 @@
 
 /// @returns the value of `base` raised to the power `exp`, or an empty optional if the operation
 /// cannot be performed.
-template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
+template <typename FloatingPointT,
+          typename = utils::traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
 inline std::optional<FloatingPointT> CheckedPow(FloatingPointT base, FloatingPointT exp) {
     static_assert(IsNumber<FloatingPointT>);
     if ((base < 0) || (base == 0 && exp <= 0)) {
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 0c00452..b7fda95 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -170,53 +170,53 @@
 
     /// Evaluates to true if T can be converted to an identifier.
     template <typename T>
-    static constexpr const bool IsIdentifierLike = std::is_same_v<T, Symbol> ||  // Symbol
-                                                   std::is_enum_v<T> ||          // Enum
-                                                   traits::IsStringLike<T>;      // String
+    static constexpr const bool IsIdentifierLike = std::is_same_v<T, Symbol> ||     // Symbol
+                                                   std::is_enum_v<T> ||             // Enum
+                                                   utils::traits::IsStringLike<T>;  // String
 
     /// A helper used to disable overloads if the first type in `TYPES` is a Source. Used to avoid
     /// ambiguities in overloads that take a Source as the first parameter and those that
     /// perfectly-forward the first argument.
     template <typename... TYPES>
-    using DisableIfSource =
-        traits::EnableIf<!IsSource<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>>;
+    using DisableIfSource = utils::traits::EnableIf<
+        !IsSource<utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>>;
 
     /// A helper used to disable overloads if the first type in `TYPES` is a scalar type. Used to
     /// avoid ambiguities in overloads that take a scalar as the first parameter and those that
     /// perfectly-forward the first argument.
     template <typename... TYPES>
-    using DisableIfScalar =
-        traits::EnableIf<!IsScalar<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>>;
+    using DisableIfScalar = utils::traits::EnableIf<
+        !IsScalar<utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>>;
 
     /// A helper used to enable overloads if the first type in `TYPES` is a scalar type. Used to
     /// avoid ambiguities in overloads that take a scalar as the first parameter and those that
     /// perfectly-forward the first argument.
     template <typename... TYPES>
-    using EnableIfScalar =
-        traits::EnableIf<IsScalar<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>>;
+    using EnableIfScalar = utils::traits::EnableIf<
+        IsScalar<utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>>;
 
     /// A helper used to disable overloads if the first type in `TYPES` is a utils::Vector,
     /// utils::VectorRef or utils::VectorRef.
     template <typename... TYPES>
-    using DisableIfVectorLike = traits::EnableIf<
-        !detail::IsVectorLike<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>::value>;
+    using DisableIfVectorLike = utils::traits::EnableIf<!detail::IsVectorLike<
+        utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>::value>;
 
     /// A helper used to enable overloads if the first type in `TYPES` is identifier-like.
     template <typename... TYPES>
-    using EnableIfIdentifierLike =
-        traits::EnableIf<IsIdentifierLike<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>>;
+    using EnableIfIdentifierLike = utils::traits::EnableIf<
+        IsIdentifierLike<utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>>;
 
     /// A helper used to disable overloads if the first type in `TYPES` is Infer or an abstract
     /// numeric.
     template <typename... TYPES>
-    using DisableIfInferOrAbstract =
-        traits::EnableIf<!IsInferOrAbstract<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>>;
+    using DisableIfInferOrAbstract = utils::traits::EnableIf<
+        !IsInferOrAbstract<utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>>;
 
     /// A helper used to enable overloads if the first type in `TYPES` is Infer or an abstract
     /// numeric.
     template <typename... TYPES>
-    using EnableIfInferOrAbstract =
-        traits::EnableIf<IsInferOrAbstract<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>>>;
+    using EnableIfInferOrAbstract = utils::traits::EnableIf<
+        IsInferOrAbstract<utils::traits::Decay<utils::traits::NthTypeOf<0, TYPES..., void>>>>;
 
     /// VarOptions is a helper for accepting an arbitrary number of order independent options for
     /// constructing an ast::Var.
@@ -258,7 +258,8 @@
         template <typename... ARGS>
         explicit LetOptions(ARGS&&... args) {
             static constexpr bool has_init =
-                (traits::IsTypeOrDerived<traits::PtrElTy<ARGS>, ast::Expression> || ...);
+                (utils::traits::IsTypeOrDerived<utils::traits::PtrElTy<ARGS>, ast::Expression> ||
+                 ...);
             static_assert(has_init, "Let() must be constructed with an initializer expression");
             (Set(std::forward<ARGS>(args)), ...);
         }
@@ -281,7 +282,8 @@
         template <typename... ARGS>
         explicit ConstOptions(ARGS&&... args) {
             static constexpr bool has_init =
-                (traits::IsTypeOrDerived<traits::PtrElTy<ARGS>, ast::Expression> || ...);
+                (utils::traits::IsTypeOrDerived<utils::traits::PtrElTy<ARGS>, ast::Expression> ||
+                 ...);
             static_assert(has_init, "Const() must be constructed with an initializer expression");
             (Set(std::forward<ARGS>(args)), ...);
         }
@@ -476,7 +478,7 @@
     /// @param args the arguments to pass to the constructor
     /// @returns the node pointer
     template <typename T, typename... ARGS>
-    traits::EnableIfIsType<T, ast::Node>* create(const Source& source, ARGS&&... args) {
+    utils::traits::EnableIfIsType<T, ast::Node>* create(const Source& source, ARGS&&... args) {
         AssertNotMoved();
         return ast_nodes_.Create<T>(id_, AllocateNodeID(), source, std::forward<ARGS>(args)...);
     }
@@ -488,7 +490,7 @@
     /// destructed.
     /// @returns the node pointer
     template <typename T>
-    traits::EnableIfIsType<T, ast::Node>* create() {
+    utils::traits::EnableIfIsType<T, ast::Node>* create() {
         AssertNotMoved();
         return ast_nodes_.Create<T>(id_, AllocateNodeID(), source_);
     }
@@ -502,10 +504,10 @@
     /// @param args the remaining arguments to pass to the constructor
     /// @returns the node pointer
     template <typename T, typename ARG0, typename... ARGS>
-    traits::EnableIf</* T is ast::Node and ARG0 is not Source */
-                     traits::IsTypeOrDerived<T, ast::Node> &&
-                         !traits::IsTypeOrDerived<ARG0, Source>,
-                     T>*
+    utils::traits::EnableIf</* T is ast::Node and ARG0 is not Source */
+                            utils::traits::IsTypeOrDerived<T, ast::Node> &&
+                                !utils::traits::IsTypeOrDerived<ARG0, Source>,
+                            T>*
     create(ARG0&& arg0, ARGS&&... args) {
         AssertNotMoved();
         return ast_nodes_.Create<T>(id_, AllocateNodeID(), source_, std::forward<ARG0>(arg0),
@@ -517,9 +519,9 @@
     /// @param args the arguments to pass to the constructor
     /// @returns the node pointer
     template <typename T, typename... ARGS>
-    traits::EnableIf<traits::IsTypeOrDerived<T, sem::Node> &&
-                         !traits::IsTypeOrDerived<T, type::Node>,
-                     T>*
+    utils::traits::EnableIf<utils::traits::IsTypeOrDerived<T, sem::Node> &&
+                                !utils::traits::IsTypeOrDerived<T, type::Node>,
+                            T>*
     create(ARGS&&... args) {
         AssertNotMoved();
         return sem_nodes_.Create<T>(std::forward<ARGS>(args)...);
@@ -530,10 +532,10 @@
     /// @param args the arguments to pass to the constructor
     /// @returns the node pointer
     template <typename T, typename... ARGS>
-    traits::EnableIf<traits::IsTypeOrDerived<T, constant::Value> &&
-                         !traits::IsTypeOrDerived<T, constant::Composite> &&
-                         !traits::IsTypeOrDerived<T, constant::Splat>,
-                     T>*
+    utils::traits::EnableIf<utils::traits::IsTypeOrDerived<T, constant::Value> &&
+                                !utils::traits::IsTypeOrDerived<T, constant::Composite> &&
+                                !utils::traits::IsTypeOrDerived<T, constant::Splat>,
+                            T>*
     create(ARGS&&... args) {
         AssertNotMoved();
         return constant_nodes_.Create<T>(std::forward<ARGS>(args)...);
@@ -547,9 +549,10 @@
     /// @param type the composite type
     /// @param elements the composite elements
     /// @returns the node pointer
-    template <typename T,
-              typename = traits::EnableIf<traits::IsTypeOrDerived<T, constant::Composite> ||
-                                          traits::IsTypeOrDerived<T, constant::Splat>>>
+    template <
+        typename T,
+        typename = utils::traits::EnableIf<utils::traits::IsTypeOrDerived<T, constant::Composite> ||
+                                           utils::traits::IsTypeOrDerived<T, constant::Splat>>>
     const constant::Value* create(const type::Type* type,
                                   utils::VectorRef<const constant::Value*> elements) {
         AssertNotMoved();
@@ -561,7 +564,9 @@
     /// @param element the splat element
     /// @param n the number of elements
     /// @returns the node pointer
-    template <typename T, typename = traits::EnableIf<traits::IsTypeOrDerived<T, constant::Splat>>>
+    template <
+        typename T,
+        typename = utils::traits::EnableIf<utils::traits::IsTypeOrDerived<T, constant::Splat>>>
     const constant::Splat* create(const type::Type* type,
                                   const constant::Value* element,
                                   size_t n) {
@@ -576,7 +581,7 @@
     /// @param args the arguments to pass to the constructor
     /// @returns the new, or existing node
     template <typename T, typename... ARGS>
-    traits::EnableIfIsType<T, type::Node>* create(ARGS&&... args) {
+    utils::traits::EnableIfIsType<T, type::Node>* create(ARGS&&... args) {
         AssertNotMoved();
         return types_.Get<T>(std::forward<ARGS>(args)...);
     }
@@ -615,7 +620,8 @@
                   typename = DisableIfSource<NAME>,
                   typename = std::enable_if_t<!std::is_same_v<std::decay_t<NAME>, ast::Type>>>
         ast::Type operator()(NAME&& name, ARGS&&... args) const {
-            if constexpr (traits::IsTypeOrDerived<traits::PtrElTy<NAME>, ast::Expression>) {
+            if constexpr (utils::traits::IsTypeOrDerived<utils::traits::PtrElTy<NAME>,
+                                                         ast::Expression>) {
                 static_assert(sizeof...(ARGS) == 0);
                 return {name};
             } else {
@@ -1479,7 +1485,8 @@
     /// @return an ast::Identifier with the given symbol
     template <typename IDENTIFIER>
     const ast::Identifier* Ident(IDENTIFIER&& identifier) {
-        if constexpr (traits::IsTypeOrDerived<traits::PtrElTy<IDENTIFIER>, ast::Identifier>) {
+        if constexpr (utils::traits::IsTypeOrDerived<utils::traits::PtrElTy<IDENTIFIER>,
+                                                     ast::Identifier>) {
             return identifier;  // Passthrough
         } else {
             return Ident(source_, std::forward<IDENTIFIER>(identifier));
@@ -1518,7 +1525,7 @@
 
     /// @param expr the expression
     /// @return expr (passthrough)
-    template <typename T, typename = traits::EnableIfIsType<T, ast::Expression>>
+    template <typename T, typename = utils::traits::EnableIfIsType<T, ast::Expression>>
     const T* Expr(const T* expr) {
         return expr;
     }
@@ -2734,8 +2741,9 @@
     const ast::MemberAccessorExpression* MemberAccessor(const Source& source,
                                                         OBJECT&& object,
                                                         MEMBER&& member) {
-        static_assert(!traits::IsType<traits::PtrElTy<MEMBER>, ast::TemplatedIdentifier>,
-                      "it is currently invalid for a structure to hold a templated member");
+        static_assert(
+            !utils::traits::IsType<utils::traits::PtrElTy<MEMBER>, ast::TemplatedIdentifier>,
+            "it is currently invalid for a structure to hold a templated member");
         return create<ast::MemberAccessorExpression>(source, Expr(std::forward<OBJECT>(object)),
                                                      Ident(std::forward<MEMBER>(member)));
     }
@@ -2882,8 +2890,8 @@
         utils::VectorRef<const ast::Attribute*> attributes = utils::Empty,
         utils::VectorRef<const ast::Attribute*> return_type_attributes = utils::Empty) {
         const ast::BlockStatement* block = nullptr;
-        using BODY_T = traits::PtrElTy<BODY>;
-        if constexpr (traits::IsTypeOrDerived<BODY_T, ast::BlockStatement> ||
+        using BODY_T = utils::traits::PtrElTy<BODY>;
+        if constexpr (utils::traits::IsTypeOrDerived<BODY_T, ast::BlockStatement> ||
                       std::is_same_v<BODY_T, std::nullptr_t>) {
             block = body;
         } else {
@@ -3753,8 +3761,9 @@
     const ast::DiagnosticAttribute* DiagnosticAttribute(const Source& source,
                                                         builtin::DiagnosticSeverity severity,
                                                         NAME&& rule_name) {
-        static_assert(!traits::IsType<traits::PtrElTy<NAME>, ast::TemplatedIdentifier>,
-                      "it is invalid for a diagnostic rule name to be templated");
+        static_assert(
+            !utils::traits::IsType<utils::traits::PtrElTy<NAME>, ast::TemplatedIdentifier>,
+            "it is invalid for a diagnostic rule name to be templated");
         return create<ast::DiagnosticAttribute>(
             source, ast::DiagnosticControl(severity, Ident(std::forward<NAME>(rule_name))));
     }
@@ -3872,7 +3881,7 @@
     /// @param args a mix of ast::Expression, ast::Statement, ast::Variables.
     /// @returns the function
     template <typename... ARGS,
-              typename = traits::EnableIf<(CanWrapInStatement<ARGS>::value && ...)>>
+              typename = utils::traits::EnableIf<(CanWrapInStatement<ARGS>::value && ...)>>
     const ast::Function* WrapInFunction(ARGS&&... args) {
         utils::Vector stmts{
             WrapInStatement(std::forward<ARGS>(args))...,
diff --git a/src/tint/reflection.h b/src/tint/reflection.h
index 0591838..d341f98 100644
--- a/src/tint/reflection.h
+++ b/src/tint/reflection.h
@@ -15,7 +15,8 @@
 #ifndef SRC_TINT_REFLECTION_H_
 #define SRC_TINT_REFLECTION_H_
 
-#include "src/tint/traits.h"
+#include <type_traits>
+
 #include "src/tint/utils/concat.h"
 #include "src/tint/utils/foreach_macro.h"
 
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index b90ea00..2aa755a 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -447,7 +447,8 @@
     auto* ty = First(cs...)->Type();
     auto* el_ty = type::Type::ElementOf(ty, &n);
     if (el_ty == ty) {
-        constexpr bool kHasIndexParam = traits::IsType<size_t, traits::LastParameterType<F>>;
+        constexpr bool kHasIndexParam =
+            utils::traits::IsType<size_t, utils::traits::LastParameterType<F>>;
         if constexpr (kHasIndexParam) {
             return f(cs..., index);
         } else {
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index ac1b9e7..c4aec90 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -29,9 +29,9 @@
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/value_expression.h"
 #include "src/tint/sem/variable.h"
-#include "src/tint/traits.h"
 #include "src/tint/type/abstract_float.h"
 #include "src/tint/type/abstract_int.h"
+#include "src/tint/utils/traits.h"
 #include "src/tint/utils/vector.h"
 
 namespace tint::resolver {
@@ -587,7 +587,7 @@
     /// @param args the value nested elements will be initialized with
     /// @return a new AST expression of the alias type
     template <bool IS_COMPOSITE = is_composite>
-    static inline traits::EnableIf<!IS_COMPOSITE, const ast::Expression*> Expr(
+    static inline utils::traits::EnableIf<!IS_COMPOSITE, const ast::Expression*> Expr(
         ProgramBuilder& b,
         utils::VectorRef<Scalar> args) {
         // Cast
@@ -598,7 +598,7 @@
     /// @param args the value nested elements will be initialized with
     /// @return a new AST expression of the alias type
     template <bool IS_COMPOSITE = is_composite>
-    static inline traits::EnableIf<IS_COMPOSITE, const ast::Expression*> Expr(
+    static inline utils::traits::EnableIf<IS_COMPOSITE, const ast::Expression*> Expr(
         ProgramBuilder& b,
         utils::VectorRef<Scalar> args) {
         // Construct
@@ -819,7 +819,7 @@
 /// Creates a Value of DataType<T> from a scalar `v`
 template <typename T>
 Value Val(T v) {
-    static_assert(traits::IsTypeIn<T, Scalar>, "v must be a Number of bool");
+    static_assert(utils::traits::IsTypeIn<T, Scalar>, "v must be a Number of bool");
     return Value::Create<T>(utils::Vector<Scalar, 1>{v});
 }
 
diff --git a/src/tint/sem/info.h b/src/tint/sem/info.h
index 29b3bc4..be50618 100644
--- a/src/tint/sem/info.h
+++ b/src/tint/sem/info.h
@@ -81,7 +81,7 @@
               typename RESULT = GetResultType<SEM, AST>>
     const RESULT* Get(const AST* ast_node) const {
         static_assert(std::is_same_v<SEM, InferFromAST> ||
-                          !traits::IsTypeOrDerived<SemanticNodeTypeFor<AST>, SEM>,
+                          !utils::traits::IsTypeOrDerived<SemanticNodeTypeFor<AST>, SEM>,
                       "explicit template argument is unnecessary");
         if (ast_node && ast_node->node_id.value < nodes_.size()) {
             return As<RESULT>(nodes_[ast_node->node_id.value]);
diff --git a/src/tint/switch.h b/src/tint/switch.h
index 41bb99a..cd2ccc8 100644
--- a/src/tint/switch.h
+++ b/src/tint/switch.h
@@ -43,13 +43,14 @@
 /// @note does not handle the Default case
 /// @see Switch().
 template <typename FN>
-using SwitchCaseType = std::remove_pointer_t<traits::ParameterType<std::remove_reference_t<FN>, 0>>;
+using SwitchCaseType =
+    std::remove_pointer_t<utils::traits::ParameterType<std::remove_reference_t<FN>, 0>>;
 
 /// Evaluates to true if the function `FN` has the signature of a Default case in a Switch().
 /// @see Switch().
 template <typename FN>
 inline constexpr bool IsDefaultCase =
-    std::is_same_v<traits::ParameterType<std::remove_reference_t<FN>, 0>, Default>;
+    std::is_same_v<utils::traits::ParameterType<std::remove_reference_t<FN>, 0>, Default>;
 
 /// Searches the list of Switch cases for a Default case, returning the index of the Default case.
 /// If the a Default case is not found in the tuple, then -1 is returned.
@@ -163,7 +164,7 @@
 /// consistent case type.
 template <typename RETURN_TYPE = detail::Infer, typename T = CastableBase, typename... CASES>
 inline auto Switch(T* object, CASES&&... cases) {
-    using ReturnType = detail::SwitchReturnType<RETURN_TYPE, traits::ReturnType<CASES>...>;
+    using ReturnType = detail::SwitchReturnType<RETURN_TYPE, utils::traits::ReturnType<CASES>...>;
     static constexpr int kDefaultIndex = detail::IndexOfDefaultCase<std::tuple<CASES...>>();
     static constexpr bool kHasDefaultCase = kDefaultIndex >= 0;
     static constexpr bool kHasReturnType = !std::is_same_v<ReturnType, void>;
diff --git a/src/tint/type/manager.h b/src/tint/type/manager.h
index 59a8cf5..0650f1b 100644
--- a/src/tint/type/manager.h
+++ b/src/tint/type/manager.h
@@ -65,9 +65,9 @@
     ///         constructed, then the same pointer is returned.
     template <typename NODE, typename... ARGS>
     NODE* Get(ARGS&&... args) {
-        if constexpr (traits::IsTypeOrDerived<NODE, Type>) {
+        if constexpr (utils::traits::IsTypeOrDerived<NODE, Type>) {
             return types_.Get<NODE>(std::forward<ARGS>(args)...);
-        } else if constexpr (traits::IsTypeOrDerived<NODE, UniqueNode>) {
+        } else if constexpr (utils::traits::IsTypeOrDerived<NODE, UniqueNode>) {
             return unique_nodes_.Get<NODE>(std::forward<ARGS>(args)...);
         } else {
             return nodes_.Create<NODE>(std::forward<ARGS>(args)...);
@@ -78,7 +78,7 @@
     /// @return a pointer to an instance of `T` with the provided arguments, or nullptr if the item
     ///         was not found.
     template <typename TYPE,
-              typename _ = std::enable_if<traits::IsTypeOrDerived<TYPE, Type>>,
+              typename _ = std::enable_if<utils::traits::IsTypeOrDerived<TYPE, Type>>,
               typename... ARGS>
     TYPE* Find(ARGS&&... args) const {
         return types_.Find<TYPE>(std::forward<ARGS>(args)...);
diff --git a/src/tint/utils/slice.h b/src/tint/utils/slice.h
index 325c470..9737cad 100644
--- a/src/tint/utils/slice.h
+++ b/src/tint/utils/slice.h
@@ -19,8 +19,8 @@
 #include <iterator>
 
 #include "src/tint/castable.h"
-#include "src/tint/traits.h"
 #include "src/tint/utils/bitcast.h"
+#include "src/tint/utils/traits.h"
 
 namespace tint::utils {
 
@@ -73,8 +73,8 @@
           // or
           //   derives from TO
           (std::is_same_v<std::remove_const_t<FROM_EL>, std::remove_const_t<TO_EL>> ||
-           (IsCastable<FROM_EL, TO_EL> &&
-            (MODE == ReinterpretMode::kUnsafe || traits::IsTypeOrDerived<FROM_EL, TO_EL>)))));
+           (IsCastable<FROM_EL, TO_EL> && (MODE == ReinterpretMode::kUnsafe ||
+                                           utils::traits::IsTypeOrDerived<FROM_EL, TO_EL>)))));
 };
 
 /// Specialization of 'CanReinterpretSlice' for when TO and FROM are equal types.
diff --git a/src/tint/traits.h b/src/tint/utils/traits.h
similarity index 97%
rename from src/tint/traits.h
rename to src/tint/utils/traits.h
index 8d74bf7..525ed9b 100644
--- a/src/tint/traits.h
+++ b/src/tint/utils/traits.h
@@ -12,15 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SRC_TINT_TRAITS_H_
-#define SRC_TINT_TRAITS_H_
+#ifndef SRC_TINT_UTILS_TRAITS_H_
+#define SRC_TINT_UTILS_TRAITS_H_
 
 #include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
 
-namespace tint::traits {
+namespace tint::utils::traits {
 
 /// Convience type definition for std::decay<T>::type
 template <typename T>
@@ -183,6 +183,6 @@
     std::is_same_v<Decay<T>, std::string> || std::is_same_v<Decay<T>, std::string_view> ||
     std::is_same_v<Decay<T>, const char*>;
 
-}  // namespace tint::traits
+}  // namespace tint::utils::traits
 
-#endif  // SRC_TINT_TRAITS_H_
+#endif  // SRC_TINT_UTILS_TRAITS_H_
diff --git a/src/tint/traits_test.cc b/src/tint/utils/traits_test.cc
similarity index 98%
rename from src/tint/traits_test.cc
rename to src/tint/utils/traits_test.cc
index e86e389..578ed44 100644
--- a/src/tint/traits_test.cc
+++ b/src/tint/utils/traits_test.cc
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/traits.h"
+#include "src/tint/utils/traits.h"
 
 #include "gtest/gtest.h"
 
-namespace tint::traits {
+namespace tint::utils::traits {
 
 namespace {
 
@@ -241,4 +241,4 @@
     static_assert(std::is_same_v<std::tuple_element_t<1, sliced>, float>);
 }
 
-}  // namespace tint::traits
+}  // namespace tint::utils::traits
diff --git a/src/tint/utils/transform.h b/src/tint/utils/transform.h
index d96a4bb..9615471 100644
--- a/src/tint/utils/transform.h
+++ b/src/tint/utils/transform.h
@@ -20,7 +20,7 @@
 #include <utility>
 #include <vector>
 
-#include "src/tint/traits.h"
+#include "src/tint/utils/traits.h"
 #include "src/tint/utils/vector.h"
 
 namespace tint::utils {