Move castable into utils.

The Castable class is used by utils/. This Cl moves the implementation
into the utils/ folder. The `Is` and `As` methods are added into the
`tint` namespace to make usage shorter.

Change-Id: I0decedb92ebed01b6aa12d2e3efa7190742e9a33
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127402
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/docs/tint/style_guide.md b/docs/tint/style_guide.md
index 25e9146..e5f8fa3 100644
--- a/docs/tint/style_guide.md
+++ b/docs/tint/style_guide.md
@@ -14,7 +14,7 @@
 * Do not use C++ exceptions
 
 * Do not use C++ RTTI.
-   Instead, use `tint::Castable::As<T>()` from
+   Instead, use `tint::utils::Castable::As<T>()` from
    [src/castable.h](../src/castable.h)
 
 * Generally, avoid `assert`.  Instead, issue a [diagnostic](../src/diagnostic.h)
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f20948d..c0a72ac 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -195,8 +195,6 @@
 
 libtint_source_set("libtint_base_src") {
   sources = [
-    "castable.cc",
-    "castable.h",
     "debug.cc",
     "debug.h",
     "diagnostic/diagnostic.cc",
@@ -222,6 +220,8 @@
     "utils/bitset.h",
     "utils/block_allocator.h",
     "utils/bump_allocator.h",
+    "utils/castable.cc",
+    "utils/castable.h",
     "utils/compiler_macros.h",
     "utils/concat.h",
     "utils/crc32.h",
@@ -1590,6 +1590,7 @@
       "utils/bitset_test.cc",
       "utils/block_allocator_test.cc",
       "utils/bump_allocator_test.cc",
+      "utils/castable_test.cc",
       "utils/crc32_test.cc",
       "utils/defer_test.cc",
       "utils/enum_set_test.cc",
@@ -1982,7 +1983,6 @@
 
   tint_unittests_source_set("tint_unittests_base_src") {
     sources = [
-      "castable_test.cc",
       "debug_test.cc",
       "number_test.cc",
       "reflection_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 820366a..628dcb3 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -230,8 +230,6 @@
   ast/while_statement.h
   ast/workgroup_attribute.cc
   ast/workgroup_attribute.h
-  castable.cc
-  castable.h
   clone_context.cc
   clone_context.h
   constant/clone_context.h
@@ -517,6 +515,8 @@
   utils/bitset.h
   utils/block_allocator.h
   utils/bump_allocator.h
+  utils/castable.cc
+  utils/castable.h
   utils/compiler_macros.h
   utils/concat.h
   utils/crc32.h
@@ -882,7 +882,6 @@
     ast/variable_test.cc
     ast/while_statement_test.cc
     ast/workgroup_attribute_test.cc
-    castable_test.cc
     clone_context_test.cc
     constant/composite_test.cc
     constant/scalar_test.cc
@@ -996,6 +995,7 @@
     utils/bitset_test.cc
     utils/block_allocator_test.cc
     utils/bump_allocator_test.cc
+    utils/castable_test.cc
     utils/crc32_test.cc
     utils/defer_test.cc
     utils/enum_set_test.cc
diff --git a/src/tint/ast/accessor_expression.h b/src/tint/ast/accessor_expression.h
index d668f1f..808ac2f 100644
--- a/src/tint/ast/accessor_expression.h
+++ b/src/tint/ast/accessor_expression.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// Base class for IndexAccessorExpression and MemberAccessorExpression
-class AccessorExpression : public Castable<AccessorExpression, Expression> {
+class AccessorExpression : public utils::Castable<AccessorExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/alias.h b/src/tint/ast/alias.h
index c30d387..519a878 100644
--- a/src/tint/ast/alias.h
+++ b/src/tint/ast/alias.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A type alias type. Holds a name and pointer to another type.
-class Alias final : public Castable<Alias, TypeDecl> {
+class Alias final : public utils::Castable<Alias, TypeDecl> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/assignment_statement.h b/src/tint/ast/assignment_statement.h
index 01a1a75..482ec4e 100644
--- a/src/tint/ast/assignment_statement.h
+++ b/src/tint/ast/assignment_statement.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// An assignment statement
-class AssignmentStatement final : public Castable<AssignmentStatement, Statement> {
+class AssignmentStatement final : public utils::Castable<AssignmentStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/attribute.h b/src/tint/ast/attribute.h
index 1809782..66c309d 100644
--- a/src/tint/ast/attribute.h
+++ b/src/tint/ast/attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// The base class for all attributes
-class Attribute : public Castable<Attribute, Node> {
+class Attribute : public utils::Castable<Attribute, Node> {
   public:
     ~Attribute() override;
 
diff --git a/src/tint/ast/binary_expression.h b/src/tint/ast/binary_expression.h
index b314250..0382a9b 100644
--- a/src/tint/ast/binary_expression.h
+++ b/src/tint/ast/binary_expression.h
@@ -43,7 +43,7 @@
 };
 
 /// An binary expression
-class BinaryExpression final : public Castable<BinaryExpression, Expression> {
+class BinaryExpression final : public utils::Castable<BinaryExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/binding_attribute.h b/src/tint/ast/binding_attribute.h
index 3ff7c97..37c8224 100644
--- a/src/tint/ast/binding_attribute.h
+++ b/src/tint/ast/binding_attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A binding attribute
-class BindingAttribute final : public Castable<BindingAttribute, Attribute> {
+class BindingAttribute final : public utils::Castable<BindingAttribute, Attribute> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/bitcast_expression.h b/src/tint/ast/bitcast_expression.h
index 7737721..d004ddf 100644
--- a/src/tint/ast/bitcast_expression.h
+++ b/src/tint/ast/bitcast_expression.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A bitcast expression
-class BitcastExpression final : public Castable<BitcastExpression, Expression> {
+class BitcastExpression final : public utils::Castable<BitcastExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/block_statement.h b/src/tint/ast/block_statement.h
index 2044b5b..8582ed3 100644
--- a/src/tint/ast/block_statement.h
+++ b/src/tint/ast/block_statement.h
@@ -27,7 +27,7 @@
 namespace tint::ast {
 
 /// A block statement
-class BlockStatement final : public Castable<BlockStatement, Statement> {
+class BlockStatement final : public utils::Castable<BlockStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/bool_literal_expression.h b/src/tint/ast/bool_literal_expression.h
index bebd924..fe509df 100644
--- a/src/tint/ast/bool_literal_expression.h
+++ b/src/tint/ast/bool_literal_expression.h
@@ -22,7 +22,8 @@
 namespace tint::ast {
 
 /// A boolean literal
-class BoolLiteralExpression final : public Castable<BoolLiteralExpression, LiteralExpression> {
+class BoolLiteralExpression final
+    : public utils::Castable<BoolLiteralExpression, LiteralExpression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/break_if_statement.h b/src/tint/ast/break_if_statement.h
index a366240..56cbabc 100644
--- a/src/tint/ast/break_if_statement.h
+++ b/src/tint/ast/break_if_statement.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A break-if statement
-class BreakIfStatement final : public Castable<BreakIfStatement, Statement> {
+class BreakIfStatement final : public utils::Castable<BreakIfStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/break_statement.h b/src/tint/ast/break_statement.h
index d9676bb..a22bf03 100644
--- a/src/tint/ast/break_statement.h
+++ b/src/tint/ast/break_statement.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// An break statement
-class BreakStatement final : public Castable<BreakStatement, Statement> {
+class BreakStatement final : public utils::Castable<BreakStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/builtin_attribute.h b/src/tint/ast/builtin_attribute.h
index 2ed6a2d..8844757 100644
--- a/src/tint/ast/builtin_attribute.h
+++ b/src/tint/ast/builtin_attribute.h
@@ -27,7 +27,7 @@
 namespace tint::ast {
 
 /// A builtin attribute
-class BuiltinAttribute final : public Castable<BuiltinAttribute, Attribute> {
+class BuiltinAttribute final : public utils::Castable<BuiltinAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/call_expression.h b/src/tint/ast/call_expression.h
index 6660494..8ad4cf1 100644
--- a/src/tint/ast/call_expression.h
+++ b/src/tint/ast/call_expression.h
@@ -29,7 +29,7 @@
 /// * sem::Builtin
 /// * sem::ValueConstructor
 /// * sem::ValueConversion
-class CallExpression final : public Castable<CallExpression, Expression> {
+class CallExpression final : public utils::Castable<CallExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/call_statement.h b/src/tint/ast/call_statement.h
index 9af47a1..ec561a1 100644
--- a/src/tint/ast/call_statement.h
+++ b/src/tint/ast/call_statement.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A call expression
-class CallStatement final : public Castable<CallStatement, Statement> {
+class CallStatement final : public utils::Castable<CallStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/case_selector.h b/src/tint/ast/case_selector.h
index f2bc1b3..e381557 100644
--- a/src/tint/ast/case_selector.h
+++ b/src/tint/ast/case_selector.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A case selector
-class CaseSelector final : public Castable<CaseSelector, Node> {
+class CaseSelector final : public utils::Castable<CaseSelector, Node> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/case_statement.h b/src/tint/ast/case_statement.h
index a06c720..fd0d18d 100644
--- a/src/tint/ast/case_statement.h
+++ b/src/tint/ast/case_statement.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A case statement
-class CaseStatement final : public Castable<CaseStatement, Statement> {
+class CaseStatement final : public utils::Castable<CaseStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/compound_assignment_statement.h b/src/tint/ast/compound_assignment_statement.h
index bb1f34f..f23af15 100644
--- a/src/tint/ast/compound_assignment_statement.h
+++ b/src/tint/ast/compound_assignment_statement.h
@@ -22,7 +22,8 @@
 namespace tint::ast {
 
 /// A compound assignment statement
-class CompoundAssignmentStatement final : public Castable<CompoundAssignmentStatement, Statement> {
+class CompoundAssignmentStatement final
+    : public utils::Castable<CompoundAssignmentStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/const.h b/src/tint/ast/const.h
index 95727ce..b008015 100644
--- a/src/tint/ast/const.h
+++ b/src/tint/ast/const.h
@@ -30,7 +30,7 @@
 ///   const max_f32 : f32 = 0x1.fffffep+127;    // f32 typed constant
 /// ```
 /// @see https://www.w3.org/TR/WGSL/#creation-time-consts
-class Const final : public Castable<Const, Variable> {
+class Const final : public utils::Castable<Const, Variable> {
   public:
     /// Create a 'const' creation-time value variable.
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/const_assert.h b/src/tint/ast/const_assert.h
index 6774f5e..8de59e3 100644
--- a/src/tint/ast/const_assert.h
+++ b/src/tint/ast/const_assert.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A `const_assert` statement
-class ConstAssert final : public Castable<ConstAssert, Statement> {
+class ConstAssert final : public utils::Castable<ConstAssert, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/continue_statement.h b/src/tint/ast/continue_statement.h
index 10af08b..338a26e 100644
--- a/src/tint/ast/continue_statement.h
+++ b/src/tint/ast/continue_statement.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// An continue statement
-class ContinueStatement final : public Castable<ContinueStatement, Statement> {
+class ContinueStatement final : public utils::Castable<ContinueStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/diagnostic_attribute.h b/src/tint/ast/diagnostic_attribute.h
index ef1fa02..fa63f83 100644
--- a/src/tint/ast/diagnostic_attribute.h
+++ b/src/tint/ast/diagnostic_attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A diagnostic attribute
-class DiagnosticAttribute final : public Castable<DiagnosticAttribute, Attribute> {
+class DiagnosticAttribute final : public utils::Castable<DiagnosticAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/diagnostic_directive.h b/src/tint/ast/diagnostic_directive.h
index 979f32c..b944384 100644
--- a/src/tint/ast/diagnostic_directive.h
+++ b/src/tint/ast/diagnostic_directive.h
@@ -29,7 +29,7 @@
 ///   // Turn off diagnostics for derivative uniformity violations.
 ///   diagnostic(off, derivative_uniformity);
 /// ```
-class DiagnosticDirective final : public Castable<DiagnosticDirective, Node> {
+class DiagnosticDirective final : public utils::Castable<DiagnosticDirective, Node> {
   public:
     /// Create a extension
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/disable_validation_attribute.h b/src/tint/ast/disable_validation_attribute.h
index 9614bc1..4cc0c08 100644
--- a/src/tint/ast/disable_validation_attribute.h
+++ b/src/tint/ast/disable_validation_attribute.h
@@ -53,7 +53,7 @@
 /// violations. Typically generated by transforms that need to produce ASTs that
 /// would otherwise cause validation errors.
 class DisableValidationAttribute final
-    : public Castable<DisableValidationAttribute, InternalAttribute> {
+    : public utils::Castable<DisableValidationAttribute, InternalAttribute> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/discard_statement.h b/src/tint/ast/discard_statement.h
index a764683..99d8ee7 100644
--- a/src/tint/ast/discard_statement.h
+++ b/src/tint/ast/discard_statement.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// A discard statement
-class DiscardStatement final : public Castable<DiscardStatement, Statement> {
+class DiscardStatement final : public utils::Castable<DiscardStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/enable.h b/src/tint/ast/enable.h
index 53f0218..ac7181a 100644
--- a/src/tint/ast/enable.h
+++ b/src/tint/ast/enable.h
@@ -28,7 +28,7 @@
 ///   // Enable an extension named "f16"
 ///   enable f16;
 /// ```
-class Enable final : public Castable<Enable, Node> {
+class Enable final : public utils::Castable<Enable, Node> {
   public:
     /// Create a extension
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/expression.h b/src/tint/ast/expression.h
index e4a94cb..e9fce0b 100644
--- a/src/tint/ast/expression.h
+++ b/src/tint/ast/expression.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// Base expression class
-class Expression : public Castable<Expression, Node> {
+class Expression : public utils::Castable<Expression, Node> {
   public:
     ~Expression() override;
 
diff --git a/src/tint/ast/extension.h b/src/tint/ast/extension.h
index 4988edb..900d491 100644
--- a/src/tint/ast/extension.h
+++ b/src/tint/ast/extension.h
@@ -24,7 +24,7 @@
 /// ```
 ///   enable f16;
 /// ```
-class Extension final : public Castable<Extension, Node> {
+class Extension final : public utils::Castable<Extension, Node> {
   public:
     /// Create a extension
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/float_literal_expression.h b/src/tint/ast/float_literal_expression.h
index 7920c78..40e7e7c 100644
--- a/src/tint/ast/float_literal_expression.h
+++ b/src/tint/ast/float_literal_expression.h
@@ -22,7 +22,8 @@
 namespace tint::ast {
 
 /// A float literal
-class FloatLiteralExpression final : public Castable<FloatLiteralExpression, LiteralExpression> {
+class FloatLiteralExpression final
+    : public utils::Castable<FloatLiteralExpression, LiteralExpression> {
   public:
     /// Literal suffix
     enum class Suffix {
diff --git a/src/tint/ast/for_loop_statement.h b/src/tint/ast/for_loop_statement.h
index 00fcf1d..fa47a29 100644
--- a/src/tint/ast/for_loop_statement.h
+++ b/src/tint/ast/for_loop_statement.h
@@ -22,7 +22,7 @@
 class Expression;
 
 /// A for loop statement
-class ForLoopStatement final : public Castable<ForLoopStatement, Statement> {
+class ForLoopStatement final : public utils::Castable<ForLoopStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/function.h b/src/tint/ast/function.h
index 8fa009f..b47d74f 100644
--- a/src/tint/ast/function.h
+++ b/src/tint/ast/function.h
@@ -38,7 +38,7 @@
 namespace tint::ast {
 
 /// A Function statement.
-class Function final : public Castable<Function, Node> {
+class Function final : public utils::Castable<Function, Node> {
   public:
     /// Create a function
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/group_attribute.h b/src/tint/ast/group_attribute.h
index af57eb5..47cef56 100644
--- a/src/tint/ast/group_attribute.h
+++ b/src/tint/ast/group_attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A group attribute
-class GroupAttribute final : public Castable<GroupAttribute, Attribute> {
+class GroupAttribute final : public utils::Castable<GroupAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/id_attribute.h b/src/tint/ast/id_attribute.h
index 5557a06..3e51ce4 100644
--- a/src/tint/ast/id_attribute.h
+++ b/src/tint/ast/id_attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// An id attribute for pipeline-overridable constants
-class IdAttribute final : public Castable<IdAttribute, Attribute> {
+class IdAttribute final : public utils::Castable<IdAttribute, Attribute> {
   public:
     /// Create an id attribute.
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/identifier.h b/src/tint/ast/identifier.h
index df138e9..3fc81df 100644
--- a/src/tint/ast/identifier.h
+++ b/src/tint/ast/identifier.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// An identifier
-class Identifier : public Castable<Identifier, Node> {
+class Identifier : public utils::Castable<Identifier, Node> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/identifier_expression.h b/src/tint/ast/identifier_expression.h
index c2200bd..839445b 100644
--- a/src/tint/ast/identifier_expression.h
+++ b/src/tint/ast/identifier_expression.h
@@ -25,7 +25,7 @@
 namespace tint::ast {
 
 /// An identifier expression
-class IdentifierExpression final : public Castable<IdentifierExpression, Expression> {
+class IdentifierExpression final : public utils::Castable<IdentifierExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/if_statement.h b/src/tint/ast/if_statement.h
index 255ce2f..3333084 100644
--- a/src/tint/ast/if_statement.h
+++ b/src/tint/ast/if_statement.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// An if statement
-class IfStatement final : public Castable<IfStatement, Statement> {
+class IfStatement final : public utils::Castable<IfStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/increment_decrement_statement.h b/src/tint/ast/increment_decrement_statement.h
index 9046e46..5fa6b93 100644
--- a/src/tint/ast/increment_decrement_statement.h
+++ b/src/tint/ast/increment_decrement_statement.h
@@ -21,7 +21,8 @@
 namespace tint::ast {
 
 /// An increment or decrement statement
-class IncrementDecrementStatement final : public Castable<IncrementDecrementStatement, Statement> {
+class IncrementDecrementStatement final
+    : public utils::Castable<IncrementDecrementStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/index_accessor_expression.h b/src/tint/ast/index_accessor_expression.h
index 09dc975..6dc6f96 100644
--- a/src/tint/ast/index_accessor_expression.h
+++ b/src/tint/ast/index_accessor_expression.h
@@ -20,7 +20,8 @@
 namespace tint::ast {
 
 /// An index accessor expression
-class IndexAccessorExpression final : public Castable<IndexAccessorExpression, AccessorExpression> {
+class IndexAccessorExpression final
+    : public utils::Castable<IndexAccessorExpression, AccessorExpression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/int_literal_expression.h b/src/tint/ast/int_literal_expression.h
index 0f50ad3..bf13f8e 100644
--- a/src/tint/ast/int_literal_expression.h
+++ b/src/tint/ast/int_literal_expression.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// An integer literal. The literal may have an 'i', 'u' or no suffix.
-class IntLiteralExpression final : public Castable<IntLiteralExpression, LiteralExpression> {
+class IntLiteralExpression final : public utils::Castable<IntLiteralExpression, LiteralExpression> {
   public:
     /// Literal suffix
     enum class Suffix {
diff --git a/src/tint/ast/internal_attribute.h b/src/tint/ast/internal_attribute.h
index 36f2a98..a06a966 100644
--- a/src/tint/ast/internal_attribute.h
+++ b/src/tint/ast/internal_attribute.h
@@ -30,7 +30,7 @@
 /// An attribute used to indicate that a function is tint-internal.
 /// These attributes are not produced by generators, but instead are usually
 /// created by transforms for consumption by a particular backend.
-class InternalAttribute : public Castable<InternalAttribute, Attribute> {
+class InternalAttribute : public utils::Castable<InternalAttribute, Attribute> {
   public:
     /// Constructor
     /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/interpolate_attribute.h b/src/tint/ast/interpolate_attribute.h
index b3177ed..65cf585 100644
--- a/src/tint/ast/interpolate_attribute.h
+++ b/src/tint/ast/interpolate_attribute.h
@@ -27,7 +27,7 @@
 namespace tint::ast {
 
 /// An interpolate attribute
-class InterpolateAttribute final : public Castable<InterpolateAttribute, Attribute> {
+class InterpolateAttribute final : public utils::Castable<InterpolateAttribute, Attribute> {
   public:
     /// Create an interpolate attribute.
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/invariant_attribute.h b/src/tint/ast/invariant_attribute.h
index 9abb6a4..b4da288 100644
--- a/src/tint/ast/invariant_attribute.h
+++ b/src/tint/ast/invariant_attribute.h
@@ -22,7 +22,7 @@
 namespace tint::ast {
 
 /// The invariant attribute
-class InvariantAttribute final : public Castable<InvariantAttribute, Attribute> {
+class InvariantAttribute final : public utils::Castable<InvariantAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/let.h b/src/tint/ast/let.h
index 127c3d4..1e529c9 100644
--- a/src/tint/ast/let.h
+++ b/src/tint/ast/let.h
@@ -27,7 +27,7 @@
 ///   let twice_depth : i32 = width + width;  // Must have initializer
 /// ```
 /// @see https://www.w3.org/TR/WGSL/#let-decls
-class Let final : public Castable<Let, Variable> {
+class Let final : public utils::Castable<Let, Variable> {
   public:
     /// Create a 'let' variable
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/literal_expression.h b/src/tint/ast/literal_expression.h
index b4b2b09..5cf5c07 100644
--- a/src/tint/ast/literal_expression.h
+++ b/src/tint/ast/literal_expression.h
@@ -22,7 +22,7 @@
 namespace tint::ast {
 
 /// Base class for a literal value expressions
-class LiteralExpression : public Castable<LiteralExpression, Expression> {
+class LiteralExpression : public utils::Castable<LiteralExpression, Expression> {
   public:
     ~LiteralExpression() override;
 
diff --git a/src/tint/ast/location_attribute.h b/src/tint/ast/location_attribute.h
index a41e943..eb10307 100644
--- a/src/tint/ast/location_attribute.h
+++ b/src/tint/ast/location_attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A location attribute
-class LocationAttribute final : public Castable<LocationAttribute, Attribute> {
+class LocationAttribute final : public utils::Castable<LocationAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/loop_statement.h b/src/tint/ast/loop_statement.h
index fc764e2..8539345 100644
--- a/src/tint/ast/loop_statement.h
+++ b/src/tint/ast/loop_statement.h
@@ -20,7 +20,7 @@
 namespace tint::ast {
 
 /// A loop statement
-class LoopStatement final : public Castable<LoopStatement, Statement> {
+class LoopStatement final : public utils::Castable<LoopStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/member_accessor_expression.h b/src/tint/ast/member_accessor_expression.h
index 95fbbd2..5ae36b1 100644
--- a/src/tint/ast/member_accessor_expression.h
+++ b/src/tint/ast/member_accessor_expression.h
@@ -22,7 +22,7 @@
 
 /// A member accessor expression
 class MemberAccessorExpression final
-    : public Castable<MemberAccessorExpression, AccessorExpression> {
+    : public utils::Castable<MemberAccessorExpression, AccessorExpression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/module.h b/src/tint/ast/module.h
index 0125fb3f..130939e 100644
--- a/src/tint/ast/module.h
+++ b/src/tint/ast/module.h
@@ -29,7 +29,7 @@
 
 /// Module holds the top-level AST types, functions and global variables used by
 /// a Program.
-class Module final : public Castable<Module, Node> {
+class Module final : public utils::Castable<Module, Node> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/must_use_attribute.h b/src/tint/ast/must_use_attribute.h
index 6befa54..a135176 100644
--- a/src/tint/ast/must_use_attribute.h
+++ b/src/tint/ast/must_use_attribute.h
@@ -22,7 +22,7 @@
 namespace tint::ast {
 
 /// The must_use attribute
-class MustUseAttribute final : public Castable<MustUseAttribute, Attribute> {
+class MustUseAttribute final : public utils::Castable<MustUseAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/node.h b/src/tint/ast/node.h
index afea2e8..3ad56f3 100644
--- a/src/tint/ast/node.h
+++ b/src/tint/ast/node.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// AST base class node
-class Node : public Castable<Node, Cloneable> {
+class Node : public utils::Castable<Node, Cloneable> {
   public:
     ~Node() override;
 
diff --git a/src/tint/ast/override.h b/src/tint/ast/override.h
index b824ed3..56fc211 100644
--- a/src/tint/ast/override.h
+++ b/src/tint/ast/override.h
@@ -30,7 +30,7 @@
 ///   override scale : f32;            // No default - must be overridden.
 /// ```
 /// @see https://www.w3.org/TR/WGSL/#override-decls
-class Override final : public Castable<Override, Variable> {
+class Override final : public utils::Castable<Override, Variable> {
   public:
     /// Create an 'override' pipeline-overridable constant.
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/parameter.h b/src/tint/ast/parameter.h
index b056afa..b363d33 100644
--- a/src/tint/ast/parameter.h
+++ b/src/tint/ast/parameter.h
@@ -31,7 +31,7 @@
 /// ```
 ///
 /// @see https://www.w3.org/TR/WGSL/#creation-time-consts
-class Parameter final : public Castable<Parameter, Variable> {
+class Parameter final : public utils::Castable<Parameter, Variable> {
   public:
     /// Create a 'parameter' creation-time value variable.
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/phony_expression.h b/src/tint/ast/phony_expression.h
index 7f59c37..764bf23 100644
--- a/src/tint/ast/phony_expression.h
+++ b/src/tint/ast/phony_expression.h
@@ -21,7 +21,7 @@
 
 /// Represents the `_` of a phony assignment `_ = <expr>`
 /// @see https://www.w3.org/TR/WGSL/#phony-assignment-section
-class PhonyExpression final : public Castable<PhonyExpression, Expression> {
+class PhonyExpression final : public utils::Castable<PhonyExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/return_statement.h b/src/tint/ast/return_statement.h
index 7eae780..6beb215 100644
--- a/src/tint/ast/return_statement.h
+++ b/src/tint/ast/return_statement.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A return statement
-class ReturnStatement final : public Castable<ReturnStatement, Statement> {
+class ReturnStatement final : public utils::Castable<ReturnStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/stage_attribute.h b/src/tint/ast/stage_attribute.h
index 0bf9d9e..9b32579 100644
--- a/src/tint/ast/stage_attribute.h
+++ b/src/tint/ast/stage_attribute.h
@@ -23,7 +23,7 @@
 namespace tint::ast {
 
 /// A workgroup attribute
-class StageAttribute final : public Castable<StageAttribute, Attribute> {
+class StageAttribute final : public utils::Castable<StageAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/statement.h b/src/tint/ast/statement.h
index fa434cc..3aeff6a 100644
--- a/src/tint/ast/statement.h
+++ b/src/tint/ast/statement.h
@@ -22,7 +22,7 @@
 namespace tint::ast {
 
 /// Base statement class
-class Statement : public Castable<Statement, Node> {
+class Statement : public utils::Castable<Statement, Node> {
   public:
     ~Statement() override;
 
diff --git a/src/tint/ast/stride_attribute.h b/src/tint/ast/stride_attribute.h
index 9014677..0d6ba18 100644
--- a/src/tint/ast/stride_attribute.h
+++ b/src/tint/ast/stride_attribute.h
@@ -24,7 +24,7 @@
 
 /// A stride attribute used by the SPIR-V reader for strided arrays and
 /// matrices.
-class StrideAttribute final : public Castable<StrideAttribute, Attribute> {
+class StrideAttribute final : public utils::Castable<StrideAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/struct.h b/src/tint/ast/struct.h
index d7c2281..067688d 100644
--- a/src/tint/ast/struct.h
+++ b/src/tint/ast/struct.h
@@ -26,7 +26,7 @@
 namespace tint::ast {
 
 /// A struct statement.
-class Struct final : public Castable<Struct, TypeDecl> {
+class Struct final : public utils::Castable<Struct, TypeDecl> {
   public:
     /// Create a new struct statement
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/struct_member.h b/src/tint/ast/struct_member.h
index 27e5b0c..a44c23d 100644
--- a/src/tint/ast/struct_member.h
+++ b/src/tint/ast/struct_member.h
@@ -28,7 +28,7 @@
 namespace tint::ast {
 
 /// A struct member statement.
-class StructMember final : public Castable<StructMember, Node> {
+class StructMember final : public utils::Castable<StructMember, Node> {
   public:
     /// Create a new struct member statement
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/struct_member_align_attribute.h b/src/tint/ast/struct_member_align_attribute.h
index 1649ae6..b368b79 100644
--- a/src/tint/ast/struct_member_align_attribute.h
+++ b/src/tint/ast/struct_member_align_attribute.h
@@ -24,7 +24,8 @@
 namespace tint::ast {
 
 /// A struct member align attribute
-class StructMemberAlignAttribute final : public Castable<StructMemberAlignAttribute, Attribute> {
+class StructMemberAlignAttribute final
+    : public utils::Castable<StructMemberAlignAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/struct_member_offset_attribute.h b/src/tint/ast/struct_member_offset_attribute.h
index ed74397..edfbb13 100644
--- a/src/tint/ast/struct_member_offset_attribute.h
+++ b/src/tint/ast/struct_member_offset_attribute.h
@@ -32,7 +32,8 @@
 /// trivial for the Resolver to handle `@offset(n)` or `@size(n)` /
 /// `@align(n)` attributes, so this is what we do, keeping all the layout
 /// logic in one place.
-class StructMemberOffsetAttribute final : public Castable<StructMemberOffsetAttribute, Attribute> {
+class StructMemberOffsetAttribute final
+    : public utils::Castable<StructMemberOffsetAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/struct_member_size_attribute.h b/src/tint/ast/struct_member_size_attribute.h
index 2a0c71f..5b2bb4e 100644
--- a/src/tint/ast/struct_member_size_attribute.h
+++ b/src/tint/ast/struct_member_size_attribute.h
@@ -24,7 +24,8 @@
 namespace tint::ast {
 
 /// A struct member size attribute
-class StructMemberSizeAttribute final : public Castable<StructMemberSizeAttribute, Attribute> {
+class StructMemberSizeAttribute final
+    : public utils::Castable<StructMemberSizeAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/switch_statement.h b/src/tint/ast/switch_statement.h
index f2b8201..f376f82 100644
--- a/src/tint/ast/switch_statement.h
+++ b/src/tint/ast/switch_statement.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A switch statement
-class SwitchStatement final : public Castable<SwitchStatement, Statement> {
+class SwitchStatement final : public utils::Castable<SwitchStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/templated_identifier.h b/src/tint/ast/templated_identifier.h
index 74ddb43..92b8345 100644
--- a/src/tint/ast/templated_identifier.h
+++ b/src/tint/ast/templated_identifier.h
@@ -26,7 +26,7 @@
 namespace tint::ast {
 
 /// A templated identifier expression
-class TemplatedIdentifier final : public Castable<TemplatedIdentifier, Identifier> {
+class TemplatedIdentifier final : public utils::Castable<TemplatedIdentifier, Identifier> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/type_decl.h b/src/tint/ast/type_decl.h
index 8ec0fe4..aa0bc14 100644
--- a/src/tint/ast/type_decl.h
+++ b/src/tint/ast/type_decl.h
@@ -25,7 +25,7 @@
 namespace tint::ast {
 
 /// The base class for type declarations.
-class TypeDecl : public Castable<TypeDecl, Node> {
+class TypeDecl : public utils::Castable<TypeDecl, Node> {
   public:
     /// Create a new struct statement
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/unary_op_expression.h b/src/tint/ast/unary_op_expression.h
index 3639447..6004975 100644
--- a/src/tint/ast/unary_op_expression.h
+++ b/src/tint/ast/unary_op_expression.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A unary op expression
-class UnaryOpExpression final : public Castable<UnaryOpExpression, Expression> {
+class UnaryOpExpression final : public utils::Castable<UnaryOpExpression, Expression> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/var.h b/src/tint/ast/var.h
index 6f08a6f..e3cfde0 100644
--- a/src/tint/ast/var.h
+++ b/src/tint/ast/var.h
@@ -39,7 +39,7 @@
 /// ```
 ///
 /// @see https://www.w3.org/TR/WGSL/#var-decls
-class Var final : public Castable<Var, Variable> {
+class Var final : public utils::Castable<Var, Variable> {
   public:
     /// Create a 'var' variable
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/variable.h b/src/tint/ast/variable.h
index 2230ac3..0a1ccb2 100644
--- a/src/tint/ast/variable.h
+++ b/src/tint/ast/variable.h
@@ -40,7 +40,7 @@
 /// declaration, "override" declaration, "const" declaration, or formal parameter to a function.
 ///
 /// @see https://www.w3.org/TR/WGSL/#value-decls
-class Variable : public Castable<Variable, Node> {
+class Variable : public utils::Castable<Variable, Node> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/variable_decl_statement.h b/src/tint/ast/variable_decl_statement.h
index 99a9a5d..7732299 100644
--- a/src/tint/ast/variable_decl_statement.h
+++ b/src/tint/ast/variable_decl_statement.h
@@ -21,7 +21,7 @@
 namespace tint::ast {
 
 /// A variable declaration statement
-class VariableDeclStatement final : public Castable<VariableDeclStatement, Statement> {
+class VariableDeclStatement final : public utils::Castable<VariableDeclStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/while_statement.h b/src/tint/ast/while_statement.h
index 360610a..6545a65 100644
--- a/src/tint/ast/while_statement.h
+++ b/src/tint/ast/while_statement.h
@@ -22,7 +22,7 @@
 class Expression;
 
 /// A while loop statement
-class WhileStatement final : public Castable<WhileStatement, Statement> {
+class WhileStatement final : public utils::Castable<WhileStatement, Statement> {
   public:
     /// Constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/workgroup_attribute.h b/src/tint/ast/workgroup_attribute.h
index 05cbb89..8789df7 100644
--- a/src/tint/ast/workgroup_attribute.h
+++ b/src/tint/ast/workgroup_attribute.h
@@ -28,7 +28,7 @@
 namespace tint::ast {
 
 /// A workgroup attribute
-class WorkgroupAttribute final : public Castable<WorkgroupAttribute, Attribute> {
+class WorkgroupAttribute final : public utils::Castable<WorkgroupAttribute, Attribute> {
   public:
     /// constructor
     /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/clone_context.cc b/src/tint/clone_context.cc
index a6f1478..f2988d1 100644
--- a/src/tint/clone_context.cc
+++ b/src/tint/clone_context.cc
@@ -97,7 +97,7 @@
     return object->Clone(this);
 }
 
-void CloneContext::CheckedCastFailure(const Cloneable* got, const TypeInfo& expected) {
+void CloneContext::CheckedCastFailure(const Cloneable* got, const utils::TypeInfo& expected) {
     TINT_ICE(Clone, Diagnostics()) << "Cloned object was not of the expected type\n"
                                    << "got:      " << got->TypeInfo().name << "\n"
                                    << "expected: " << expected.name;
diff --git a/src/tint/clone_context.h b/src/tint/clone_context.h
index 9012982..5f0073c 100644
--- a/src/tint/clone_context.h
+++ b/src/tint/clone_context.h
@@ -21,10 +21,10 @@
 #include <utility>
 #include <vector>
 
-#include "src/tint/castable.h"
 #include "src/tint/debug.h"
 #include "src/tint/program_id.h"
 #include "src/tint/symbol.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/hashmap.h"
 #include "src/tint/utils/hashset.h"
@@ -49,7 +49,7 @@
 ProgramID ProgramIDOf(const ProgramBuilder*);
 
 /// Cloneable is the base class for all objects that can be cloned
-class Cloneable : public Castable<Cloneable> {
+class Cloneable : public utils::Castable<Cloneable> {
   public:
     /// Constructor
     Cloneable();
@@ -308,18 +308,18 @@
         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>()) ||
-                                      TypeInfo::Of<T>().Is(transform.typeinfo);
+            bool already_registered = transform.typeinfo->Is(&utils::TypeInfo::Of<T>()) ||
+                                      utils::TypeInfo::Of<T>().Is(transform.typeinfo);
             if (TINT_UNLIKELY(already_registered)) {
-                TINT_ICE(Clone, Diagnostics())
-                    << "ReplaceAll() called with a handler for type " << TypeInfo::Of<T>().name
-                    << " that is already handled by a handler for type "
-                    << transform.typeinfo->name;
+                TINT_ICE(Clone, Diagnostics()) << "ReplaceAll() called with a handler for type "
+                                               << utils::TypeInfo::Of<T>().name
+                                               << " that is already handled by a handler for type "
+                                               << transform.typeinfo->name;
                 return *this;
             }
         }
         CloneableTransform transform;
-        transform.typeinfo = &TypeInfo::Of<T>();
+        transform.typeinfo = &utils::TypeInfo::Of<T>();
         transform.function = [=](const Cloneable* in) { return replacer(in->As<T>()); };
         transforms_.Push(std::move(transform));
         return *this;
@@ -554,8 +554,8 @@
         /// Destructor
         ~CloneableTransform();
 
-        // TypeInfo of the Cloneable that the transform operates on
-        const TypeInfo* typeinfo;
+        // utils::TypeInfo of the Cloneable that the transform operates on
+        const utils::TypeInfo* typeinfo;
         std::function<const Cloneable*(const Cloneable*)> function;
     };
 
@@ -598,7 +598,7 @@
         if (TINT_LIKELY(cast)) {
             return cast;
         }
-        CheckedCastFailure(obj, TypeInfo::Of<TO>());
+        CheckedCastFailure(obj, utils::TypeInfo::Of<TO>());
         return nullptr;
     }
 
@@ -608,7 +608,7 @@
 
     /// Adds an error diagnostic to Diagnostics() that the cloned object was not
     /// of the expected type.
-    void CheckedCastFailure(const Cloneable* got, const TypeInfo& expected);
+    void CheckedCastFailure(const Cloneable* got, const utils::TypeInfo& expected);
 
     /// @returns the diagnostic list of #dst
     diag::List& Diagnostics() const;
diff --git a/src/tint/clone_context_test.cc b/src/tint/clone_context_test.cc
index ec17e0d..4df5ade 100644
--- a/src/tint/clone_context_test.cc
+++ b/src/tint/clone_context_test.cc
@@ -30,7 +30,7 @@
     utils::BlockAllocator<Cloneable> alloc;
 };
 
-struct Node : public Castable<Node, Cloneable> {
+struct Node : public utils::Castable<Node, Cloneable> {
     Node(Allocator* alloc,
          Symbol n,
          const Node* node_a = nullptr,
@@ -55,7 +55,7 @@
     }
 };
 
-struct Replaceable : public Castable<Replaceable, Node> {
+struct Replaceable : public utils::Castable<Replaceable, Node> {
     Replaceable(Allocator* alloc,
                 Symbol n,
                 const Node* node_a = nullptr,
@@ -64,18 +64,18 @@
         : Base(alloc, n, node_a, node_b, node_c) {}
 };
 
-struct Replacement : public Castable<Replacement, Replaceable> {
+struct Replacement : public utils::Castable<Replacement, Replaceable> {
     Replacement(Allocator* alloc, Symbol n) : Base(alloc, n) {}
 };
 
-struct NotANode : public Castable<NotANode, Cloneable> {
+struct NotANode : public utils::Castable<NotANode, Cloneable> {
     explicit NotANode(Allocator* alloc) : allocator(alloc) {}
 
     Allocator* const allocator;
     NotANode* Clone(CloneContext*) const override { return allocator->Create<NotANode>(); }
 };
 
-struct ProgramNode : public Castable<ProgramNode, Cloneable> {
+struct ProgramNode : public utils::Castable<ProgramNode, Cloneable> {
     ProgramNode(Allocator* alloc, ProgramID id, ProgramID cloned_id)
         : allocator(alloc), program_id(id), cloned_program_id(cloned_id) {}
 
@@ -1051,7 +1051,7 @@
 }
 
 TEST_F(CloneContextNodeTest, CloneWithReplaceAll_SameTypeTwice) {
-    std::string node_name = TypeInfo::Of<Node>().name;
+    std::string node_name = utils::TypeInfo::Of<Node>().name;
 
     EXPECT_FATAL_FAILURE(
         {
@@ -1066,8 +1066,8 @@
 }
 
 TEST_F(CloneContextNodeTest, CloneWithReplaceAll_BaseThenDerived) {
-    std::string node_name = TypeInfo::Of<Node>().name;
-    std::string replaceable_name = TypeInfo::Of<Replaceable>().name;
+    std::string node_name = utils::TypeInfo::Of<Node>().name;
+    std::string replaceable_name = utils::TypeInfo::Of<Replaceable>().name;
 
     EXPECT_FATAL_FAILURE(
         {
@@ -1082,8 +1082,8 @@
 }
 
 TEST_F(CloneContextNodeTest, CloneWithReplaceAll_DerivedThenBase) {
-    std::string node_name = TypeInfo::Of<Node>().name;
-    std::string replaceable_name = TypeInfo::Of<Replaceable>().name;
+    std::string node_name = utils::TypeInfo::Of<Node>().name;
+    std::string replaceable_name = utils::TypeInfo::Of<Replaceable>().name;
 
     EXPECT_FATAL_FAILURE(
         {
diff --git a/src/tint/constant/composite.h b/src/tint/constant/composite.h
index ef7233c..7a50640 100644
--- a/src/tint/constant/composite.h
+++ b/src/tint/constant/composite.h
@@ -15,10 +15,10 @@
 #ifndef SRC_TINT_CONSTANT_COMPOSITE_H_
 #define SRC_TINT_CONSTANT_COMPOSITE_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/constant/value.h"
 #include "src/tint/number.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/hash.h"
 #include "src/tint/utils/vector.h"
 
@@ -28,7 +28,7 @@
 /// Composite may be of a vector, matrix, array or structure type.
 /// If each element is the same type and value, then a Splat would be a more efficient constant
 /// implementation. Use CreateComposite() to create the appropriate type.
-class Composite : public Castable<Composite, Value> {
+class Composite : public utils::Castable<Composite, Value> {
   public:
     /// Constructor
     /// @param t the compsite type
diff --git a/src/tint/constant/node.h b/src/tint/constant/node.h
index 41d00e09..54ef94d 100644
--- a/src/tint/constant/node.h
+++ b/src/tint/constant/node.h
@@ -15,12 +15,12 @@
 #ifndef SRC_TINT_CONSTANT_NODE_H_
 #define SRC_TINT_CONSTANT_NODE_H_
 
-#include "src/tint/castable.h"
+#include "src/tint/utils/castable.h"
 
 namespace tint::constant {
 
 /// Node is the base class for all constant nodes
-class Node : public Castable<Node> {
+class Node : public utils::Castable<Node> {
   public:
     /// Constructor
     Node();
diff --git a/src/tint/constant/scalar.h b/src/tint/constant/scalar.h
index fc6aef0..ab5f852 100644
--- a/src/tint/constant/scalar.h
+++ b/src/tint/constant/scalar.h
@@ -15,17 +15,17 @@
 #ifndef SRC_TINT_CONSTANT_SCALAR_H_
 #define SRC_TINT_CONSTANT_SCALAR_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/constant/value.h"
 #include "src/tint/number.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/hash.h"
 
 namespace tint::constant {
 
 /// Scalar holds a single scalar or abstract-numeric value.
 template <typename T>
-class Scalar : public Castable<Scalar<T>, Value> {
+class Scalar : public utils::Castable<Scalar<T>, Value> {
   public:
     static_assert(!std::is_same_v<UnwrapNumber<T>, T> || std::is_same_v<T, bool>,
                   "T must be a Number or bool");
diff --git a/src/tint/constant/splat.h b/src/tint/constant/splat.h
index a496235..d8e55a6 100644
--- a/src/tint/constant/splat.h
+++ b/src/tint/constant/splat.h
@@ -15,9 +15,9 @@
 #ifndef SRC_TINT_CONSTANT_SPLAT_H_
 #define SRC_TINT_CONSTANT_SPLAT_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/constant/composite.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/vector.h"
 
 namespace tint::constant {
@@ -26,7 +26,7 @@
 ///
 /// Splat is used for zero-initializers, 'splat' initializers, or initializers where each element is
 /// identical. Splat may be of a vector, matrix, array or structure type.
-class Splat : public Castable<Splat, Value> {
+class Splat : public utils::Castable<Splat, Value> {
   public:
     /// Constructor
     /// @param t the splat type
diff --git a/src/tint/constant/value.h b/src/tint/constant/value.h
index eb29998..fe1f78a 100644
--- a/src/tint/constant/value.h
+++ b/src/tint/constant/value.h
@@ -17,16 +17,16 @@
 
 #include <variant>
 
-#include "src/tint/castable.h"
 #include "src/tint/constant/clone_context.h"
 #include "src/tint/constant/node.h"
 #include "src/tint/number.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 
 namespace tint::constant {
 
 /// Value is the interface to a compile-time evaluated expression value.
-class Value : public Castable<Value, Node> {
+class Value : public utils::Castable<Value, Node> {
   public:
     /// Constructor
     Value();
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/util.h b/src/tint/fuzzers/tint_ast_fuzzer/util.h
index fe114f4..b428d46 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/util.h
+++ b/src/tint/fuzzers/tint_ast_fuzzer/util.h
@@ -19,12 +19,12 @@
 
 #include "src/tint/ast/module.h"
 #include "src/tint/ast/variable_decl_statement.h"
-#include "src/tint/castable.h"
 #include "src/tint/program.h"
 #include "src/tint/sem/block_statement.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/utils/castable.h"
 
 namespace tint::fuzzers::ast_fuzzer::util {
 /// @file
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index f2749f3..246943d 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -456,7 +456,7 @@
 
 std::vector<ResourceBinding> Inspector::GetTextureResourceBindings(
     const std::string& entry_point,
-    const tint::TypeInfo* texture_type,
+    const tint::utils::TypeInfo* texture_type,
     ResourceBinding::ResourceType resource_type) {
     auto* func = FindEntryPointByName(entry_point);
     if (!func) {
@@ -485,19 +485,20 @@
 
 std::vector<ResourceBinding> Inspector::GetDepthTextureResourceBindings(
     const std::string& entry_point) {
-    return GetTextureResourceBindings(entry_point, &TypeInfo::Of<type::DepthTexture>(),
+    return GetTextureResourceBindings(entry_point, &utils::TypeInfo::Of<type::DepthTexture>(),
                                       ResourceBinding::ResourceType::kDepthTexture);
 }
 
 std::vector<ResourceBinding> Inspector::GetDepthMultisampledTextureResourceBindings(
     const std::string& entry_point) {
-    return GetTextureResourceBindings(entry_point, &TypeInfo::Of<type::DepthMultisampledTexture>(),
+    return GetTextureResourceBindings(entry_point,
+                                      &utils::TypeInfo::Of<type::DepthMultisampledTexture>(),
                                       ResourceBinding::ResourceType::kDepthMultisampledTexture);
 }
 
 std::vector<ResourceBinding> Inspector::GetExternalTextureResourceBindings(
     const std::string& entry_point) {
-    return GetTextureResourceBindings(entry_point, &TypeInfo::Of<type::ExternalTexture>(),
+    return GetTextureResourceBindings(entry_point, &utils::TypeInfo::Of<type::ExternalTexture>(),
                                       ResourceBinding::ResourceType::kExternalTexture);
 }
 
diff --git a/src/tint/inspector/inspector.h b/src/tint/inspector/inspector.h
index 443bbab..a4cfe6e 100644
--- a/src/tint/inspector/inspector.h
+++ b/src/tint/inspector/inspector.h
@@ -195,7 +195,7 @@
     /// @returns vector of all of the bindings for depth textures.
     std::vector<ResourceBinding> GetTextureResourceBindings(
         const std::string& entry_point,
-        const tint::TypeInfo* texture_type,
+        const tint::utils::TypeInfo* texture_type,
         ResourceBinding::ResourceType resource_type);
 
     /// @param entry_point name of the entry point to get information about.
diff --git a/src/tint/ir/binary.h b/src/tint/ir/binary.h
index bb74ea8..e39758f 100644
--- a/src/tint/ir/binary.h
+++ b/src/tint/ir/binary.h
@@ -15,16 +15,16 @@
 #ifndef SRC_TINT_IR_BINARY_H_
 #define SRC_TINT_IR_BINARY_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/instruction.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// An instruction in the IR.
-class Binary : public Castable<Binary, Instruction> {
+class Binary : public utils::Castable<Binary, Instruction> {
   public:
     /// The kind of instruction.
     enum class Kind {
diff --git a/src/tint/ir/bitcast.h b/src/tint/ir/bitcast.h
index 7e9f7db..7dcb495 100644
--- a/src/tint/ir/bitcast.h
+++ b/src/tint/ir/bitcast.h
@@ -15,16 +15,16 @@
 #ifndef SRC_TINT_IR_BITCAST_H_
 #define SRC_TINT_IR_BITCAST_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/instruction.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// A bitcast instruction in the IR.
-class Bitcast : public Castable<Bitcast, Instruction> {
+class Bitcast : public utils::Castable<Bitcast, Instruction> {
   public:
     /// Constructor
     /// @param result the result value
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index 4258167..e83d43a 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -25,7 +25,7 @@
 /// A flow node comprising a block of statements. The instructions in the block are a linear list of
 /// instructions to execute. The block will branch at the end. The only blocks which do not branch
 /// are the end blocks of functions.
-class Block : public Castable<Block, FlowNode> {
+class Block : public utils::Castable<Block, FlowNode> {
   public:
     /// Constructor
     Block();
diff --git a/src/tint/ir/builtin.h b/src/tint/ir/builtin.h
index 4fc8a76..ee73ec6 100644
--- a/src/tint/ir/builtin.h
+++ b/src/tint/ir/builtin.h
@@ -16,16 +16,16 @@
 #define SRC_TINT_IR_BUILTIN_H_
 
 #include "src/tint/builtin/function.h"
-#include "src/tint/castable.h"
 #include "src/tint/ir/call.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// A value conversion instruction in the IR.
-class Builtin : public Castable<Builtin, Call> {
+class Builtin : public utils::Castable<Builtin, Call> {
   public:
     /// Constructor
     /// @param result the result value
diff --git a/src/tint/ir/call.h b/src/tint/ir/call.h
index e1f9d48..89c07cd 100644
--- a/src/tint/ir/call.h
+++ b/src/tint/ir/call.h
@@ -15,16 +15,16 @@
 #ifndef SRC_TINT_IR_CALL_H_
 #define SRC_TINT_IR_CALL_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/instruction.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// A Call instruction in the IR.
-class Call : public Castable<Call, Instruction> {
+class Call : public utils::Castable<Call, Instruction> {
   public:
     /// Constructor
     /// @param result the result value
diff --git a/src/tint/ir/constant.h b/src/tint/ir/constant.h
index 7be3688..dd50fac 100644
--- a/src/tint/ir/constant.h
+++ b/src/tint/ir/constant.h
@@ -23,7 +23,7 @@
 namespace tint::ir {
 
 /// Constant in the IR.
-class Constant : public Castable<Constant, Value> {
+class Constant : public utils::Castable<Constant, Value> {
   public:
     /// Constructor
     /// @param val the value stored in the constant
diff --git a/src/tint/ir/construct.h b/src/tint/ir/construct.h
index 24449b6..f6c7a0a 100644
--- a/src/tint/ir/construct.h
+++ b/src/tint/ir/construct.h
@@ -15,16 +15,16 @@
 #ifndef SRC_TINT_IR_CONSTRUCT_H_
 #define SRC_TINT_IR_CONSTRUCT_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/call.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// A constructor instruction in the IR.
-class Construct : public Castable<Construct, Call> {
+class Construct : public utils::Castable<Construct, Call> {
   public:
     /// Constructor
     /// @param result the result value
diff --git a/src/tint/ir/convert.h b/src/tint/ir/convert.h
index 3b8878b..15d118e 100644
--- a/src/tint/ir/convert.h
+++ b/src/tint/ir/convert.h
@@ -15,16 +15,16 @@
 #ifndef SRC_TINT_IR_CONVERT_H_
 #define SRC_TINT_IR_CONVERT_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/call.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// A value conversion instruction in the IR.
-class Convert : public Castable<Convert, Call> {
+class Convert : public utils::Castable<Convert, Call> {
   public:
     /// Constructor
     /// @param result the result value
diff --git a/src/tint/ir/flow_node.h b/src/tint/ir/flow_node.h
index 019a8a0..2a91674 100644
--- a/src/tint/ir/flow_node.h
+++ b/src/tint/ir/flow_node.h
@@ -15,13 +15,13 @@
 #ifndef SRC_TINT_IR_FLOW_NODE_H_
 #define SRC_TINT_IR_FLOW_NODE_H_
 
-#include "src/tint/castable.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/vector.h"
 
 namespace tint::ir {
 
 /// Base class for flow nodes
-class FlowNode : public Castable<FlowNode> {
+class FlowNode : public utils::Castable<FlowNode> {
   public:
     ~FlowNode() override;
 
diff --git a/src/tint/ir/function.h b/src/tint/ir/function.h
index c6032ba..ab3c6eb 100644
--- a/src/tint/ir/function.h
+++ b/src/tint/ir/function.h
@@ -27,7 +27,7 @@
 namespace tint::ir {
 
 /// An IR representation of a function
-class Function : public Castable<Function, FlowNode> {
+class Function : public utils::Castable<Function, FlowNode> {
   public:
     /// Constructor
     Function();
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 823700e..255b165 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -27,7 +27,7 @@
 namespace tint::ir {
 
 /// A flow node representing an if statement.
-class If : public Castable<If, FlowNode> {
+class If : public utils::Castable<If, FlowNode> {
   public:
     /// Constructor
     If();
diff --git a/src/tint/ir/instruction.h b/src/tint/ir/instruction.h
index 8ae2de4..8f58e54 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -15,15 +15,15 @@
 #ifndef SRC_TINT_IR_INSTRUCTION_H_
 #define SRC_TINT_IR_INSTRUCTION_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/value.h"
 #include "src/tint/symbol_table.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// An instruction in the IR.
-class Instruction : public Castable<Instruction> {
+class Instruction : public utils::Castable<Instruction> {
   public:
     Instruction(const Instruction& instr) = delete;
     Instruction(Instruction&& instr) = delete;
diff --git a/src/tint/ir/loop.h b/src/tint/ir/loop.h
index 2b026e6..e0066f4 100644
--- a/src/tint/ir/loop.h
+++ b/src/tint/ir/loop.h
@@ -22,7 +22,7 @@
 namespace tint::ir {
 
 /// Flow node describing a loop.
-class Loop : public Castable<Loop, FlowNode> {
+class Loop : public utils::Castable<Loop, FlowNode> {
   public:
     /// Constructor
     Loop();
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index 6ba1d4c..2ff927e 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -24,7 +24,7 @@
 namespace tint::ir {
 
 /// Flow node representing a switch statement
-class Switch : public Castable<Switch, FlowNode> {
+class Switch : public utils::Castable<Switch, FlowNode> {
   public:
     /// A case selector
     struct CaseSelector {
diff --git a/src/tint/ir/temp.h b/src/tint/ir/temp.h
index 9433099..31bc284 100644
--- a/src/tint/ir/temp.h
+++ b/src/tint/ir/temp.h
@@ -22,7 +22,7 @@
 namespace tint::ir {
 
 /// Temporary value in the IR.
-class Temp : public Castable<Temp, Value> {
+class Temp : public utils::Castable<Temp, Value> {
   public:
     /// A value id.
     using Id = uint32_t;
diff --git a/src/tint/ir/terminator.h b/src/tint/ir/terminator.h
index 9f9aba0..35fbcee 100644
--- a/src/tint/ir/terminator.h
+++ b/src/tint/ir/terminator.h
@@ -21,7 +21,7 @@
 
 /// Flow node used as the end of a function. Must only be used as the `end_target` in a function
 /// flow node. There are no instructions and no branches from this node.
-class Terminator : public Castable<Terminator, FlowNode> {
+class Terminator : public utils::Castable<Terminator, FlowNode> {
   public:
     /// Constructor
     Terminator();
diff --git a/src/tint/ir/user_call.h b/src/tint/ir/user_call.h
index 34215ed..8c99d89 100644
--- a/src/tint/ir/user_call.h
+++ b/src/tint/ir/user_call.h
@@ -15,16 +15,16 @@
 #ifndef SRC_TINT_IR_USER_CALL_H_
 #define SRC_TINT_IR_USER_CALL_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/ir/call.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 
 namespace tint::ir {
 
 /// A user call instruction in the IR.
-class UserCall : public Castable<UserCall, Call> {
+class UserCall : public utils::Castable<UserCall, Call> {
   public:
     /// Constructor
     /// @param result the result value
diff --git a/src/tint/ir/value.h b/src/tint/ir/value.h
index 2d4fcb0..b498e9d 100644
--- a/src/tint/ir/value.h
+++ b/src/tint/ir/value.h
@@ -15,9 +15,9 @@
 #ifndef SRC_TINT_IR_VALUE_H_
 #define SRC_TINT_IR_VALUE_H_
 
-#include "src/tint/castable.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/type/type.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/string_stream.h"
 #include "src/tint/utils/unique_vector.h"
 
@@ -29,7 +29,7 @@
 namespace tint::ir {
 
 /// Value in the IR.
-class Value : public Castable<Value> {
+class Value : public utils::Castable<Value> {
   public:
     /// Destructor
     ~Value() override;
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index e729a17..8061f74 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -693,7 +693,8 @@
 
 /// A StatementBuilder for ast::SwitchStatement
 /// @see StatementBuilder
-struct SwitchStatementBuilder final : public Castable<SwitchStatementBuilder, StatementBuilder> {
+struct SwitchStatementBuilder final
+    : public utils::Castable<SwitchStatementBuilder, StatementBuilder> {
     /// Constructor
     /// @param cond the switch statement condition
     explicit SwitchStatementBuilder(const ast::Expression* cond) : condition(cond) {}
@@ -717,7 +718,7 @@
 
 /// A StatementBuilder for ast::IfStatement
 /// @see StatementBuilder
-struct IfStatementBuilder final : public Castable<IfStatementBuilder, StatementBuilder> {
+struct IfStatementBuilder final : public utils::Castable<IfStatementBuilder, StatementBuilder> {
     /// Constructor
     /// @param c the if-statement condition
     explicit IfStatementBuilder(const ast::Expression* c) : cond(c) {}
@@ -738,7 +739,7 @@
 
 /// A StatementBuilder for ast::LoopStatement
 /// @see StatementBuilder
-struct LoopStatementBuilder final : public Castable<LoopStatementBuilder, StatementBuilder> {
+struct LoopStatementBuilder final : public utils::Castable<LoopStatementBuilder, StatementBuilder> {
     /// @param builder the program builder
     /// @returns the built ast::LoopStatement
     ast::LoopStatement* Build(ProgramBuilder* builder) const override {
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index 11fc92c..19452b1 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -407,7 +407,7 @@
 /// become immutable. The builders may hold mutable state while the
 /// StatementBlock is being constructed, which becomes an immutable node on
 /// StatementBlock::Finalize().
-class StatementBuilder : public Castable<StatementBuilder, ast::Statement> {
+class StatementBuilder : public utils::Castable<StatementBuilder, ast::Statement> {
   public:
     /// Constructor
     StatementBuilder() : Base(ProgramID(), ast::NodeID(), Source{}) {}
diff --git a/src/tint/reader/spirv/parser_type.h b/src/tint/reader/spirv/parser_type.h
index 8623be5..d3295ee 100644
--- a/src/tint/reader/spirv/parser_type.h
+++ b/src/tint/reader/spirv/parser_type.h
@@ -23,11 +23,11 @@
 #include "src/tint/builtin/access.h"
 #include "src/tint/builtin/address_space.h"
 #include "src/tint/builtin/texel_format.h"
-#include "src/tint/castable.h"
 #include "src/tint/symbol.h"
 #include "src/tint/type/sampler_kind.h"
 #include "src/tint/type/texture_dimension.h"
 #include "src/tint/utils/block_allocator.h"
+#include "src/tint/utils/castable.h"
 
 // Forward declarations
 namespace tint {
@@ -37,7 +37,7 @@
 namespace tint::reader::spirv {
 
 /// Type is the base class for all types
-class Type : public Castable<Type> {
+class Type : public utils::Castable<Type> {
   public:
     /// Constructor
     Type();
@@ -97,7 +97,7 @@
 using TypeList = std::vector<const Type*>;
 
 /// `void` type
-struct Void final : public Castable<Void, Type> {
+struct Void final : public utils::Castable<Void, Type> {
     /// @param b the ProgramBuilder used to construct the AST types
     /// @returns the constructed ast::Type node for the given type
     ast::Type Build(ProgramBuilder& b) const override;
@@ -109,7 +109,7 @@
 };
 
 /// `bool` type
-struct Bool final : public Castable<Bool, Type> {
+struct Bool final : public utils::Castable<Bool, Type> {
     /// @param b the ProgramBuilder used to construct the AST types
     /// @returns the constructed ast::Type node for the given type
     ast::Type Build(ProgramBuilder& b) const override;
@@ -121,7 +121,7 @@
 };
 
 /// `u32` type
-struct U32 final : public Castable<U32, Type> {
+struct U32 final : public utils::Castable<U32, Type> {
     /// @param b the ProgramBuilder used to construct the AST types
     /// @returns the constructed ast::Type node for the given type
     ast::Type Build(ProgramBuilder& b) const override;
@@ -133,7 +133,7 @@
 };
 
 /// `f32` type
-struct F32 final : public Castable<F32, Type> {
+struct F32 final : public utils::Castable<F32, Type> {
     /// @param b the ProgramBuilder used to construct the AST types
     /// @returns the constructed ast::Type node for the given type
     ast::Type Build(ProgramBuilder& b) const override;
@@ -145,7 +145,7 @@
 };
 
 /// `i32` type
-struct I32 final : public Castable<I32, Type> {
+struct I32 final : public utils::Castable<I32, Type> {
     /// @param b the ProgramBuilder used to construct the AST types
     /// @returns the constructed ast::Type node for the given type
     ast::Type Build(ProgramBuilder& b) const override;
@@ -157,7 +157,7 @@
 };
 
 /// `ptr<SC, T, AM>` type
-struct Pointer final : public Castable<Pointer, Type> {
+struct Pointer final : public utils::Castable<Pointer, Type> {
     /// Constructor
     /// @param ty the store type
     /// @param sc the pointer address space
@@ -188,7 +188,7 @@
 /// `ref<SC, T, AM>` type
 /// Note this has no AST representation, but is used for type tracking in the
 /// reader.
-struct Reference final : public Castable<Reference, Type> {
+struct Reference final : public utils::Castable<Reference, Type> {
     /// Constructor
     /// @param ty the referenced type
     /// @param sc the reference address space
@@ -217,7 +217,7 @@
 };
 
 /// `vecN<T>` type
-struct Vector final : public Castable<Vector, Type> {
+struct Vector final : public utils::Castable<Vector, Type> {
     /// Constructor
     /// @param ty the element type
     /// @param sz the number of elements in the vector
@@ -243,7 +243,7 @@
 };
 
 /// `matNxM<T>` type
-struct Matrix final : public Castable<Matrix, Type> {
+struct Matrix final : public utils::Castable<Matrix, Type> {
     /// Constructor
     /// @param ty the matrix element type
     /// @param c the number of columns in the matrix
@@ -272,7 +272,7 @@
 };
 
 /// `array<T, N>` type
-struct Array final : public Castable<Array, Type> {
+struct Array final : public utils::Castable<Array, Type> {
     /// Constructor
     /// @param el the element type
     /// @param sz the number of elements in the array. 0 represents runtime-sized
@@ -302,7 +302,7 @@
 };
 
 /// `sampler` type
-struct Sampler final : public Castable<Sampler, Type> {
+struct Sampler final : public utils::Castable<Sampler, Type> {
     /// Constructor
     /// @param k the sampler kind
     explicit Sampler(type::SamplerKind k);
@@ -325,7 +325,7 @@
 };
 
 /// Base class for texture types
-struct Texture : public Castable<Texture, Type> {
+struct Texture : public utils::Castable<Texture, Type> {
     ~Texture() override;
 
     /// Constructor
@@ -341,7 +341,7 @@
 };
 
 /// `texture_depth_D` type
-struct DepthTexture final : public Castable<DepthTexture, Texture> {
+struct DepthTexture final : public utils::Castable<DepthTexture, Texture> {
     /// Constructor
     /// @param d the texture dimensions
     explicit DepthTexture(type::TextureDimension d);
@@ -361,7 +361,7 @@
 };
 
 /// `texture_depth_multisampled_D` type
-struct DepthMultisampledTexture final : public Castable<DepthMultisampledTexture, Texture> {
+struct DepthMultisampledTexture final : public utils::Castable<DepthMultisampledTexture, Texture> {
     /// Constructor
     /// @param d the texture dimensions
     explicit DepthMultisampledTexture(type::TextureDimension d);
@@ -381,7 +381,7 @@
 };
 
 /// `texture_multisampled_D<T>` type
-struct MultisampledTexture final : public Castable<MultisampledTexture, Texture> {
+struct MultisampledTexture final : public utils::Castable<MultisampledTexture, Texture> {
     /// Constructor
     /// @param d the texture dimensions
     /// @param t the multisampled texture type
@@ -405,7 +405,7 @@
 };
 
 /// `texture_D<T>` type
-struct SampledTexture final : public Castable<SampledTexture, Texture> {
+struct SampledTexture final : public utils::Castable<SampledTexture, Texture> {
     /// Constructor
     /// @param d the texture dimensions
     /// @param t the sampled texture type
@@ -429,7 +429,7 @@
 };
 
 /// `texture_storage_D<F>` type
-struct StorageTexture final : public Castable<StorageTexture, Texture> {
+struct StorageTexture final : public utils::Castable<StorageTexture, Texture> {
     /// Constructor
     /// @param d the texture dimensions
     /// @param f the storage image format
@@ -457,7 +457,7 @@
 };
 
 /// Base class for named types
-struct Named : public Castable<Named, Type> {
+struct Named : public utils::Castable<Named, Type> {
     /// Constructor
     /// @param n the type name
     explicit Named(Symbol n);
@@ -479,7 +479,7 @@
 };
 
 /// `type T = N` type
-struct Alias final : public Castable<Alias, Named> {
+struct Alias final : public utils::Castable<Alias, Named> {
     /// Constructor
     /// @param n the alias name
     /// @param t the aliased type
@@ -498,7 +498,7 @@
 };
 
 /// `struct N { ... };` type
-struct Struct final : public Castable<Struct, Named> {
+struct Struct final : public utils::Castable<Struct, Named> {
     /// Constructor
     /// @param n the struct name
     /// @param m the member types
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 45dca0b..d6a2f0f 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -2185,7 +2185,7 @@
 namespace InternalAttributeDeps {
 namespace {
 
-class TestAttribute : public Castable<TestAttribute, ast::InternalAttribute> {
+class TestAttribute : public utils::Castable<TestAttribute, ast::InternalAttribute> {
   public:
     TestAttribute(ProgramID pid, ast::NodeID nid, const ast::IdentifierExpression* dep)
         : Base(pid, nid, utils::Vector{dep}) {}
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 62dde26..6aa050b 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -58,7 +58,7 @@
 constexpr static const size_t kNumFixedCandidates = 8;
 
 /// A special type that matches all TypeMatchers
-class Any final : public Castable<Any, type::Type> {
+class Any final : public utils::Castable<Any, type::Type> {
   public:
     Any() : Base(0u, type::Flags{}) {}
     ~Any() override = default;
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index bfe45c9..1ec680d 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -771,7 +771,7 @@
 
 void Resolver::SetShadows() {
     for (auto it : dependencies_.shadows) {
-        CastableBase* b = sem_.Get(it.value);
+        utils::CastableBase* b = sem_.Get(it.value);
         if (TINT_UNLIKELY(!b)) {
             TINT_ICE(Resolver, diagnostics_)
                 << "AST node '" << it.value->TypeInfo().name << "' had no semantic info\n"
@@ -4265,7 +4265,8 @@
 SEM* Resolver::StatementScope(const ast::Statement* ast, SEM* sem, F&& callback) {
     builder_->Sem().Add(ast, sem);
 
-    auto* as_compound = As<sem::CompoundStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
+    auto* as_compound =
+        As<sem::CompoundStatement, utils::CastFlags::kDontErrorOnImpossibleCast>(sem);
 
     // Helper to handle attributes that are supported on certain types of statement.
     auto handle_attributes = [&](auto* stmt, sem::Statement* sem_stmt, const char* use) {
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 6485a6a..6b84d85 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -50,13 +50,13 @@
 
 using ResolverValidationTest = ResolverTest;
 
-class FakeStmt final : public Castable<FakeStmt, ast::Statement> {
+class FakeStmt final : public utils::Castable<FakeStmt, ast::Statement> {
   public:
     FakeStmt(ProgramID pid, ast::NodeID nid, Source src) : Base(pid, nid, src) {}
     FakeStmt* Clone(CloneContext*) const override { return nullptr; }
 };
 
-class FakeExpr final : public Castable<FakeExpr, ast::Expression> {
+class FakeExpr final : public utils::Castable<FakeExpr, ast::Expression> {
   public:
     FakeExpr(ProgramID pid, ast::NodeID nid, Source src) : Base(pid, nid, src) {}
     FakeExpr* Clone(CloneContext*) const override { return nullptr; }
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 36d5139..f72000b 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -2508,7 +2508,7 @@
 }
 
 bool Validator::NoDuplicateAttributes(utils::VectorRef<const ast::Attribute*> attributes) const {
-    utils::Hashmap<const TypeInfo*, Source, 8> seen;
+    utils::Hashmap<const utils::TypeInfo*, Source, 8> seen;
     utils::Vector<const ast::DiagnosticControl*, 8> diagnostic_controls;
     for (auto* d : attributes) {
         if (auto* diag = d->As<ast::DiagnosticAttribute>()) {
diff --git a/src/tint/sem/array_count.cc b/src/tint/sem/array_count.cc
index 90abb69..2f7d1e1 100644
--- a/src/tint/sem/array_count.cc
+++ b/src/tint/sem/array_count.cc
@@ -22,7 +22,7 @@
 namespace tint::sem {
 
 NamedOverrideArrayCount::NamedOverrideArrayCount(const GlobalVariable* var)
-    : Base(static_cast<size_t>(TypeInfo::Of<NamedOverrideArrayCount>().full_hashcode)),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<NamedOverrideArrayCount>().full_hashcode)),
       variable(var) {}
 NamedOverrideArrayCount::~NamedOverrideArrayCount() = default;
 
@@ -43,7 +43,8 @@
 }
 
 UnnamedOverrideArrayCount::UnnamedOverrideArrayCount(const ValueExpression* e)
-    : Base(static_cast<size_t>(TypeInfo::Of<UnnamedOverrideArrayCount>().full_hashcode)), expr(e) {}
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<UnnamedOverrideArrayCount>().full_hashcode)),
+      expr(e) {}
 UnnamedOverrideArrayCount::~UnnamedOverrideArrayCount() = default;
 
 bool UnnamedOverrideArrayCount::Equals(const UniqueNode& other) const {
diff --git a/src/tint/sem/array_count.h b/src/tint/sem/array_count.h
index a4143d8..9f0cd53 100644
--- a/src/tint/sem/array_count.h
+++ b/src/tint/sem/array_count.h
@@ -29,7 +29,8 @@
 /// override N : i32;
 /// type arr = array<i32, N>
 /// ```
-class NamedOverrideArrayCount final : public Castable<NamedOverrideArrayCount, type::ArrayCount> {
+class NamedOverrideArrayCount final
+    : public utils::Castable<NamedOverrideArrayCount, type::ArrayCount> {
   public:
     /// Constructor
     /// @param var the `override` variable
@@ -58,7 +59,7 @@
 /// type arr = array<i32, N*2>
 /// ```
 class UnnamedOverrideArrayCount final
-    : public Castable<UnnamedOverrideArrayCount, type::ArrayCount> {
+    : public utils::Castable<UnnamedOverrideArrayCount, type::ArrayCount> {
   public:
     /// Constructor
     /// @param e the override expression
diff --git a/src/tint/sem/block_statement.h b/src/tint/sem/block_statement.h
index e5d4969..9cf1e3e 100644
--- a/src/tint/sem/block_statement.h
+++ b/src/tint/sem/block_statement.h
@@ -31,7 +31,7 @@
 
 /// Holds semantic information about a block, such as parent block and variables
 /// declared in the block.
-class BlockStatement : public Castable<BlockStatement, CompoundStatement> {
+class BlockStatement : public utils::Castable<BlockStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this block statement
@@ -50,7 +50,8 @@
 };
 
 /// The root block statement for a function
-class FunctionBlockStatement final : public Castable<FunctionBlockStatement, BlockStatement> {
+class FunctionBlockStatement final
+    : public utils::Castable<FunctionBlockStatement, BlockStatement> {
   public:
     /// Constructor
     /// @param function the owning function
@@ -61,7 +62,7 @@
 };
 
 /// Holds semantic information about a loop body block or for-loop body block
-class LoopBlockStatement final : public Castable<LoopBlockStatement, BlockStatement> {
+class LoopBlockStatement final : public utils::Castable<LoopBlockStatement, BlockStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this block statement
diff --git a/src/tint/sem/break_if_statement.h b/src/tint/sem/break_if_statement.h
index c5b62de..dcab9bb 100644
--- a/src/tint/sem/break_if_statement.h
+++ b/src/tint/sem/break_if_statement.h
@@ -28,7 +28,7 @@
 namespace tint::sem {
 
 /// Holds semantic information about a break-if statement
-class BreakIfStatement final : public Castable<BreakIfStatement, CompoundStatement> {
+class BreakIfStatement final : public utils::Castable<BreakIfStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this break-if statement
diff --git a/src/tint/sem/builtin.h b/src/tint/sem/builtin.h
index b535e72..07c3ad1 100644
--- a/src/tint/sem/builtin.h
+++ b/src/tint/sem/builtin.h
@@ -77,7 +77,7 @@
 bool IsDP4aBuiltin(builtin::Function i);
 
 /// Builtin holds the semantic information for a builtin function.
-class Builtin final : public Castable<Builtin, CallTarget> {
+class Builtin final : public utils::Castable<Builtin, CallTarget> {
   public:
     /// Constructor
     /// @param type the builtin type
diff --git a/src/tint/sem/builtin_enum_expression.h b/src/tint/sem/builtin_enum_expression.h
index 3a403fb..86c70c3 100644
--- a/src/tint/sem/builtin_enum_expression.h
+++ b/src/tint/sem/builtin_enum_expression.h
@@ -26,7 +26,7 @@
 
 /// Base class for BuiltinEnumExpression.
 /// Useful for Is() queries.
-class BuiltinEnumExpressionBase : public Castable<BuiltinEnumExpressionBase, Expression> {
+class BuiltinEnumExpressionBase : public utils::Castable<BuiltinEnumExpressionBase, Expression> {
   public:
     /// Constructor
     /// @param declaration the AST node
@@ -41,7 +41,7 @@
 /// builtin enumerator value.
 template <typename ENUM>
 class BuiltinEnumExpression
-    : public Castable<BuiltinEnumExpression<ENUM>, BuiltinEnumExpressionBase> {
+    : public utils::Castable<BuiltinEnumExpression<ENUM>, BuiltinEnumExpressionBase> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/call.h b/src/tint/sem/call.h
index 3079eb1..f24899d 100644
--- a/src/tint/sem/call.h
+++ b/src/tint/sem/call.h
@@ -26,7 +26,7 @@
 
 /// Call is the base class for semantic nodes that hold semantic information for
 /// ast::CallExpression nodes.
-class Call final : public Castable<Call, ValueExpression> {
+class Call final : public utils::Castable<Call, ValueExpression> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/call_target.h b/src/tint/sem/call_target.h
index 096aa8f..0bcb5d2 100644
--- a/src/tint/sem/call_target.h
+++ b/src/tint/sem/call_target.h
@@ -67,7 +67,7 @@
 
 /// CallTarget is the base for callable functions, builtins, value constructors and value
 /// conversions.
-class CallTarget : public Castable<CallTarget, Node> {
+class CallTarget : public utils::Castable<CallTarget, Node> {
   public:
     /// Constructor
     /// @param stage the earliest evaluation stage for a call to this target
diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h
index 884ff4d..36b4b71 100644
--- a/src/tint/sem/expression.h
+++ b/src/tint/sem/expression.h
@@ -26,7 +26,7 @@
 namespace tint::sem {
 
 /// Expression holds the semantic information for expression nodes.
-class Expression : public Castable<Expression, Node> {
+class Expression : public utils::Castable<Expression, Node> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/for_loop_statement.h b/src/tint/sem/for_loop_statement.h
index 7e22912..154e00d 100644
--- a/src/tint/sem/for_loop_statement.h
+++ b/src/tint/sem/for_loop_statement.h
@@ -28,7 +28,7 @@
 namespace tint::sem {
 
 /// Holds semantic information about a for-loop statement
-class ForLoopStatement final : public Castable<ForLoopStatement, CompoundStatement> {
+class ForLoopStatement final : public utils::Castable<ForLoopStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this for-loop statement
diff --git a/src/tint/sem/function.cc b/src/tint/sem/function.cc
index 9484012..eab78e0 100644
--- a/src/tint/sem/function.cc
+++ b/src/tint/sem/function.cc
@@ -114,7 +114,7 @@
 }
 
 Function::VariableBindings Function::TransitivelyReferencedVariablesOfType(
-    const tint::TypeInfo* type) const {
+    const tint::utils::TypeInfo* type) const {
     VariableBindings ret;
     for (auto* global : TransitivelyReferencedGlobals()) {
         auto* unwrapped_type = global->Type()->UnwrapRef();
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index b157e7d..31390f7 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -47,7 +47,7 @@
 using WorkgroupSize = std::array<std::optional<uint32_t>, 3>;
 
 /// Function holds the semantic information for function nodes.
-class Function final : public Castable<Function, CallTarget> {
+class Function final : public utils::Castable<Function, CallTarget> {
   public:
     /// A vector of [Variable*, sem::BindingPoint] pairs
     using VariableBindings = std::vector<std::pair<const Variable*, sem::BindingPoint>>;
@@ -219,14 +219,14 @@
     /// must be decorated with both binding and group attributes.
     /// @param type the type of the variables to find
     /// @returns the referenced variables
-    VariableBindings TransitivelyReferencedVariablesOfType(const tint::TypeInfo* type) const;
+    VariableBindings TransitivelyReferencedVariablesOfType(const tint::utils::TypeInfo* type) const;
 
     /// Retrieves any referenced variables of the given type. Note, the variables
     /// must be decorated with both binding and group attributes.
     /// @returns the referenced variables
     template <typename T>
     VariableBindings TransitivelyReferencedVariablesOfType() const {
-        return TransitivelyReferencedVariablesOfType(&TypeInfo::Of<T>());
+        return TransitivelyReferencedVariablesOfType(&utils::TypeInfo::Of<T>());
     }
 
     /// Checks if the given entry point is an ancestor
diff --git a/src/tint/sem/function_expression.h b/src/tint/sem/function_expression.h
index de5ac5a..9d9e8ec 100644
--- a/src/tint/sem/function_expression.h
+++ b/src/tint/sem/function_expression.h
@@ -26,7 +26,7 @@
 
 /// FunctionExpression holds the semantic information for expression nodes that resolve to
 /// functions.
-class FunctionExpression : public Castable<FunctionExpression, Expression> {
+class FunctionExpression : public utils::Castable<FunctionExpression, Expression> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/if_statement.h b/src/tint/sem/if_statement.h
index 5b27fba..42146fc 100644
--- a/src/tint/sem/if_statement.h
+++ b/src/tint/sem/if_statement.h
@@ -28,7 +28,7 @@
 namespace tint::sem {
 
 /// Holds semantic information about an if statement
-class IfStatement final : public Castable<IfStatement, CompoundStatement> {
+class IfStatement final : public utils::Castable<IfStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this if statement
diff --git a/src/tint/sem/index_accessor_expression.h b/src/tint/sem/index_accessor_expression.h
index 8118659..c2379f5 100644
--- a/src/tint/sem/index_accessor_expression.h
+++ b/src/tint/sem/index_accessor_expression.h
@@ -23,7 +23,8 @@
 namespace tint::sem {
 
 /// IndexAccessorExpression holds the semantic information for a ast::IndexAccessorExpression node.
-class IndexAccessorExpression final : public Castable<IndexAccessorExpression, ValueExpression> {
+class IndexAccessorExpression final
+    : public utils::Castable<IndexAccessorExpression, ValueExpression> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/info.h b/src/tint/sem/info.h
index be50618..9e5a139 100644
--- a/src/tint/sem/info.h
+++ b/src/tint/sem/info.h
@@ -77,7 +77,7 @@
     /// @param ast_node the AST node
     /// @returns a pointer to the semantic node if found, otherwise nullptr
     template <typename SEM = InferFromAST,
-              typename AST = CastableBase,
+              typename AST = utils::CastableBase,
               typename RESULT = GetResultType<SEM, AST>>
     const RESULT* Get(const AST* ast_node) const {
         static_assert(std::is_same_v<SEM, InferFromAST> ||
@@ -141,7 +141,8 @@
     /// Records that this variable (transitively) references the given override variable.
     /// @param from the item the variable is referenced from
     /// @param var the module-scope override variable
-    void AddTransitivelyReferencedOverride(const CastableBase* from, const GlobalVariable* var) {
+    void AddTransitivelyReferencedOverride(const utils::CastableBase* from,
+                                           const GlobalVariable* var) {
         if (referenced_overrides_.count(from) == 0) {
             referenced_overrides_.insert({from, TransitivelyReferenced{}});
         }
@@ -150,7 +151,8 @@
 
     /// @param from the key to look up
     /// @returns all transitively referenced override variables or nullptr if none set
-    const TransitivelyReferenced* TransitivelyReferencedOverrides(const CastableBase* from) const {
+    const TransitivelyReferenced* TransitivelyReferencedOverrides(
+        const utils::CastableBase* from) const {
         if (referenced_overrides_.count(from) == 0) {
             return nullptr;
         }
@@ -166,9 +168,9 @@
 
   private:
     // AST node index to semantic node
-    std::vector<const CastableBase*> nodes_;
+    std::vector<const utils::CastableBase*> nodes_;
     // Lists transitively referenced overrides for the given item
-    std::unordered_map<const CastableBase*, TransitivelyReferenced> referenced_overrides_;
+    std::unordered_map<const utils::CastableBase*, TransitivelyReferenced> referenced_overrides_;
     // The semantic module
     sem::Module* module_ = nullptr;
 };
diff --git a/src/tint/sem/load.h b/src/tint/sem/load.h
index e02e050..674b631 100644
--- a/src/tint/sem/load.h
+++ b/src/tint/sem/load.h
@@ -23,7 +23,7 @@
 /// Load is a semantic expression which represents the load of a reference to a non-reference value.
 /// Loads from reference types are implicit in WGSL, so the Load semantic node shares the same AST
 /// node as the inner semantic node.
-class Load final : public Castable<Load, ValueExpression> {
+class Load final : public utils::Castable<Load, ValueExpression> {
   public:
     /// Constructor
     /// @param reference the reference expression being loaded
diff --git a/src/tint/sem/loop_statement.h b/src/tint/sem/loop_statement.h
index 6dc7037..c09bbb5 100644
--- a/src/tint/sem/loop_statement.h
+++ b/src/tint/sem/loop_statement.h
@@ -25,7 +25,7 @@
 namespace tint::sem {
 
 /// Holds semantic information about a loop statement
-class LoopStatement final : public Castable<LoopStatement, CompoundStatement> {
+class LoopStatement final : public utils::Castable<LoopStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this loop statement
@@ -41,7 +41,7 @@
 
 /// Holds semantic information about a loop continuing block
 class LoopContinuingBlockStatement final
-    : public Castable<LoopContinuingBlockStatement, BlockStatement> {
+    : public utils::Castable<LoopContinuingBlockStatement, BlockStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this block statement
diff --git a/src/tint/sem/materialize.h b/src/tint/sem/materialize.h
index 4ce1190..c7b612f 100644
--- a/src/tint/sem/materialize.h
+++ b/src/tint/sem/materialize.h
@@ -25,7 +25,7 @@
 /// the same AST node as the inner semantic node.
 /// Abstract numerics types may only be used by compile-time expressions, so a Materialize semantic
 /// node must have a valid Constant value.
-class Materialize final : public Castable<Materialize, ValueExpression> {
+class Materialize final : public utils::Castable<Materialize, ValueExpression> {
   public:
     /// Constructor
     /// @param expr the inner expression, being materialized
diff --git a/src/tint/sem/member_accessor_expression.h b/src/tint/sem/member_accessor_expression.h
index 7ed0a41..cea9f2d 100644
--- a/src/tint/sem/member_accessor_expression.h
+++ b/src/tint/sem/member_accessor_expression.h
@@ -30,7 +30,7 @@
 
 /// MemberAccessorExpression holds the semantic information for a
 /// ast::MemberAccessorExpression node.
-class MemberAccessorExpression : public Castable<MemberAccessorExpression, ValueExpression> {
+class MemberAccessorExpression : public utils::Castable<MemberAccessorExpression, ValueExpression> {
   public:
     /// Destructor
     ~MemberAccessorExpression() override;
@@ -64,7 +64,8 @@
 /// StructMemberAccess holds the semantic information for a
 /// ast::MemberAccessorExpression node that represents an access to a structure
 /// member.
-class StructMemberAccess final : public Castable<StructMemberAccess, MemberAccessorExpression> {
+class StructMemberAccess final
+    : public utils::Castable<StructMemberAccess, MemberAccessorExpression> {
   public:
     /// Constructor
     /// @param declaration the AST node
@@ -96,7 +97,7 @@
 
 /// Swizzle holds the semantic information for a ast::MemberAccessorExpression
 /// node that represents a vector swizzle.
-class Swizzle final : public Castable<Swizzle, MemberAccessorExpression> {
+class Swizzle final : public utils::Castable<Swizzle, MemberAccessorExpression> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/module.h b/src/tint/sem/module.h
index ba4e0a9..172c22b 100644
--- a/src/tint/sem/module.h
+++ b/src/tint/sem/module.h
@@ -29,7 +29,7 @@
 
 /// Module holds the top-level semantic types, functions and global variables
 /// used by a Program.
-class Module final : public Castable<Module, Node> {
+class Module final : public utils::Castable<Module, Node> {
   public:
     /// Constructor
     /// @param dep_ordered_decls the dependency-ordered module-scope declarations
diff --git a/src/tint/sem/node.h b/src/tint/sem/node.h
index 3f2df55..9a1c706 100644
--- a/src/tint/sem/node.h
+++ b/src/tint/sem/node.h
@@ -15,12 +15,12 @@
 #ifndef SRC_TINT_SEM_NODE_H_
 #define SRC_TINT_SEM_NODE_H_
 
-#include "src/tint/castable.h"
+#include "src/tint/utils/castable.h"
 
 namespace tint::sem {
 
 /// Node is the base class for all semantic nodes
-class Node : public Castable<Node> {
+class Node : public utils::Castable<Node> {
   public:
     /// Constructor
     Node();
diff --git a/src/tint/sem/statement.h b/src/tint/sem/statement.h
index 373b9f6..a0eeb44 100644
--- a/src/tint/sem/statement.h
+++ b/src/tint/sem/statement.h
@@ -57,7 +57,7 @@
 }  // namespace detail
 
 /// Statement holds the semantic information for a statement.
-class Statement : public Castable<Statement, Node> {
+class Statement : public utils::Castable<Statement, Node> {
   public:
     /// Constructor
     /// @param declaration the AST node for this statement
@@ -133,7 +133,7 @@
 
 /// CompoundStatement is the base class of statements that can hold other
 /// statements.
-class CompoundStatement : public Castable<Statement, Statement> {
+class CompoundStatement : public utils::Castable<Statement, Statement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this statement
diff --git a/src/tint/sem/struct.h b/src/tint/sem/struct.h
index d1d20cc..2b437af 100644
--- a/src/tint/sem/struct.h
+++ b/src/tint/sem/struct.h
@@ -38,7 +38,7 @@
 namespace tint::sem {
 
 /// Struct holds the semantic information for structures.
-class Struct final : public Castable<Struct, type::Struct> {
+class Struct final : public utils::Castable<Struct, type::Struct> {
   public:
     /// Constructor
     /// @param declaration the AST structure declaration
@@ -73,7 +73,7 @@
 };
 
 /// StructMember holds the semantic information for structure members.
-class StructMember final : public Castable<StructMember, type::StructMember> {
+class StructMember final : public utils::Castable<StructMember, type::StructMember> {
   public:
     /// Constructor
     /// @param declaration the AST declaration node
diff --git a/src/tint/sem/switch_statement.h b/src/tint/sem/switch_statement.h
index 503d0a5..68c754f 100644
--- a/src/tint/sem/switch_statement.h
+++ b/src/tint/sem/switch_statement.h
@@ -37,7 +37,7 @@
 namespace tint::sem {
 
 /// Holds semantic information about an switch statement
-class SwitchStatement final : public Castable<SwitchStatement, CompoundStatement> {
+class SwitchStatement final : public utils::Castable<SwitchStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this switch statement
@@ -64,7 +64,7 @@
 };
 
 /// Holds semantic information about a switch case statement
-class CaseStatement final : public Castable<CaseStatement, CompoundStatement> {
+class CaseStatement final : public utils::Castable<CaseStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this case statement
@@ -98,7 +98,7 @@
 };
 
 /// Holds semantic information about a switch case selector
-class CaseSelector final : public Castable<CaseSelector, Node> {
+class CaseSelector final : public utils::Castable<CaseSelector, Node> {
   public:
     /// Constructor
     /// @param decl the selector declaration
diff --git a/src/tint/sem/type_expression.h b/src/tint/sem/type_expression.h
index 0d4e43b..981be69 100644
--- a/src/tint/sem/type_expression.h
+++ b/src/tint/sem/type_expression.h
@@ -25,7 +25,7 @@
 namespace tint::sem {
 
 /// TypeExpression holds the semantic information for expression nodes that resolve to types.
-class TypeExpression : public Castable<TypeExpression, Expression> {
+class TypeExpression : public utils::Castable<TypeExpression, Expression> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/type_mappings.h b/src/tint/sem/type_mappings.h
index efcd3a6..4f31254 100644
--- a/src/tint/sem/type_mappings.h
+++ b/src/tint/sem/type_mappings.h
@@ -20,9 +20,9 @@
 #include "src/tint/sem/builtin_enum_expression.h"
 
 // Forward declarations
-namespace tint {
+namespace tint::utils {
 class CastableBase;
-}  // namespace tint
+}  // namespace tint::utils
 namespace tint::ast {
 class AccessorExpression;
 class BinaryExpression;
@@ -78,7 +78,7 @@
 struct TypeMappings {
     //! @cond Doxygen_Suppress
     BuiltinEnumExpression<builtin::BuiltinValue>* operator()(ast::BuiltinAttribute*);
-    CastableBase* operator()(ast::Node*);
+    utils::CastableBase* operator()(ast::Node*);
     Expression* operator()(ast::Expression*);
     ForLoopStatement* operator()(ast::ForLoopStatement*);
     Function* operator()(ast::Function*);
diff --git a/src/tint/sem/value_constructor.h b/src/tint/sem/value_constructor.h
index 34c3b43..02fff3b 100644
--- a/src/tint/sem/value_constructor.h
+++ b/src/tint/sem/value_constructor.h
@@ -21,7 +21,7 @@
 namespace tint::sem {
 
 /// ValueConstructor is the CallTarget for a value constructor.
-class ValueConstructor final : public Castable<ValueConstructor, CallTarget> {
+class ValueConstructor final : public utils::Castable<ValueConstructor, CallTarget> {
   public:
     /// Constructor
     /// @param type the type that's being constructed
diff --git a/src/tint/sem/value_conversion.h b/src/tint/sem/value_conversion.h
index 2d2ab38..890856e 100644
--- a/src/tint/sem/value_conversion.h
+++ b/src/tint/sem/value_conversion.h
@@ -20,7 +20,7 @@
 namespace tint::sem {
 
 /// ValueConversion is the CallTarget for a value conversion (cast).
-class ValueConversion final : public Castable<ValueConversion, CallTarget> {
+class ValueConversion final : public utils::Castable<ValueConversion, CallTarget> {
   public:
     /// Constructor
     /// @param type the target type of the cast
diff --git a/src/tint/sem/value_expression.h b/src/tint/sem/value_expression.h
index a6fb624..2059ae3 100644
--- a/src/tint/sem/value_expression.h
+++ b/src/tint/sem/value_expression.h
@@ -29,7 +29,7 @@
 namespace tint::sem {
 
 /// ValueExpression holds the semantic information for expression nodes.
-class ValueExpression : public Castable<ValueExpression, Expression> {
+class ValueExpression : public utils::Castable<ValueExpression, Expression> {
   public:
     /// Constructor
     /// @param declaration the AST node
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index 7e70ca3..4cc0b1e 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -45,7 +45,7 @@
 
 /// Variable is the base class for local variables, global variables and
 /// parameters.
-class Variable : public Castable<Variable, Node> {
+class Variable : public utils::Castable<Variable, Node> {
   public:
     /// Constructor
     /// @param declaration the AST declaration node
@@ -108,7 +108,7 @@
 };
 
 /// LocalVariable is a function-scope variable
-class LocalVariable final : public Castable<LocalVariable, Variable> {
+class LocalVariable final : public utils::Castable<LocalVariable, Variable> {
   public:
     /// Constructor
     /// @param declaration the AST declaration node
@@ -133,19 +133,19 @@
     const sem::Statement* Statement() const { return statement_; }
 
     /// @returns the Type, Function or Variable that this local variable shadows
-    const CastableBase* Shadows() const { return shadows_; }
+    const utils::CastableBase* Shadows() const { return shadows_; }
 
     /// Sets the Type, Function or Variable that this local variable shadows
     /// @param shadows the Type, Function or Variable that this variable shadows
-    void SetShadows(const CastableBase* shadows) { shadows_ = shadows; }
+    void SetShadows(const utils::CastableBase* shadows) { shadows_ = shadows; }
 
   private:
     const sem::Statement* const statement_;
-    const CastableBase* shadows_ = nullptr;
+    const utils::CastableBase* shadows_ = nullptr;
 };
 
 /// GlobalVariable is a module-scope variable
-class GlobalVariable final : public Castable<GlobalVariable, Variable> {
+class GlobalVariable final : public utils::Castable<GlobalVariable, Variable> {
   public:
     /// Constructor
     /// @param declaration the AST declaration node
@@ -191,7 +191,7 @@
 };
 
 /// Parameter is a function parameter
-class Parameter final : public Castable<Parameter, Variable> {
+class Parameter final : public utils::Castable<Parameter, Variable> {
   public:
     /// Constructor for function parameters
     /// @param declaration the AST declaration node
@@ -232,11 +232,11 @@
     void SetOwner(CallTarget const* owner) { owner_ = owner; }
 
     /// @returns the Type, Function or Variable that this local variable shadows
-    const CastableBase* Shadows() const { return shadows_; }
+    const utils::CastableBase* Shadows() const { return shadows_; }
 
     /// Sets the Type, Function or Variable that this local variable shadows
     /// @param shadows the Type, Function or Variable that this variable shadows
-    void SetShadows(const CastableBase* shadows) { shadows_ = shadows; }
+    void SetShadows(const utils::CastableBase* shadows) { shadows_ = shadows; }
 
     /// @returns the resource binding point for the parameter
     std::optional<sem::BindingPoint> BindingPoint() const { return binding_point_; }
@@ -248,14 +248,14 @@
     const uint32_t index_;
     const ParameterUsage usage_;
     CallTarget const* owner_ = nullptr;
-    const CastableBase* shadows_ = nullptr;
+    const utils::CastableBase* shadows_ = nullptr;
     const std::optional<sem::BindingPoint> binding_point_;
     const std::optional<uint32_t> location_;
 };
 
 /// VariableUser holds the semantic information for an identifier expression
 /// node that resolves to a variable.
-class VariableUser final : public Castable<VariableUser, ValueExpression> {
+class VariableUser final : public utils::Castable<VariableUser, ValueExpression> {
   public:
     /// Constructor
     /// @param declaration the AST identifier node
diff --git a/src/tint/sem/while_statement.h b/src/tint/sem/while_statement.h
index fa136bd..75d9d93 100644
--- a/src/tint/sem/while_statement.h
+++ b/src/tint/sem/while_statement.h
@@ -28,7 +28,7 @@
 namespace tint::sem {
 
 /// Holds semantic information about a while statement
-class WhileStatement final : public Castable<WhileStatement, CompoundStatement> {
+class WhileStatement final : public utils::Castable<WhileStatement, CompoundStatement> {
   public:
     /// Constructor
     /// @param declaration the AST node for this while statement
diff --git a/src/tint/switch.h b/src/tint/switch.h
index cd2ccc8..df5bd95 100644
--- a/src/tint/switch.h
+++ b/src/tint/switch.h
@@ -18,8 +18,8 @@
 #include <tuple>
 #include <utility>
 
-#include "src/tint/castable.h"
 #include "src/tint/utils/bitcast.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/defer.h"
 
 namespace tint {
@@ -67,7 +67,7 @@
 
 /// Resolves to T if T is not nullptr_t, otherwise resolves to Ignore.
 template <typename T>
-using NullptrToIgnore = std::conditional_t<std::is_same_v<T, std::nullptr_t>, Ignore, T>;
+using NullptrToIgnore = std::conditional_t<std::is_same_v<T, std::nullptr_t>, utils::Ignore, T>;
 
 /// Resolves to `const TYPE` if any of `CASE_RETURN_TYPES` are const or pointer-to-const, otherwise
 /// resolves to TYPE.
@@ -92,7 +92,7 @@
 
 /// SwitchReturnTypeImpl specialization for non-castable case types and an inferred return type.
 template <typename... CASE_RETURN_TYPES>
-struct SwitchReturnTypeImpl</*IS_CASTABLE*/ false, Infer, CASE_RETURN_TYPES...> {
+struct SwitchReturnTypeImpl</*IS_CASTABLE*/ false, utils::detail::Infer, CASE_RETURN_TYPES...> {
     /// Resolves to the common type for all the cases return types.
     using type = std::common_type_t<CASE_RETURN_TYPES...>;
 };
@@ -108,10 +108,10 @@
 
 /// SwitchReturnTypeImpl specialization for castable case types and an inferred return type.
 template <typename... CASE_RETURN_TYPES>
-struct SwitchReturnTypeImpl</*IS_CASTABLE*/ true, Infer, CASE_RETURN_TYPES...> {
+struct SwitchReturnTypeImpl</*IS_CASTABLE*/ true, utils::detail::Infer, CASE_RETURN_TYPES...> {
   private:
-    using InferredType =
-        CastableCommonBase<detail::NullptrToIgnore<std::remove_pointer_t<CASE_RETURN_TYPES>>...>;
+    using InferredType = utils::CastableCommonBase<
+        detail::NullptrToIgnore<std::remove_pointer_t<CASE_RETURN_TYPES>>...>;
 
   public:
     /// `const T*` or `T*`, where T is the common base type for all the castable case types.
@@ -123,7 +123,7 @@
 /// from the case return types.
 template <typename REQUESTED_TYPE, typename... CASE_RETURN_TYPES>
 using SwitchReturnType = typename SwitchReturnTypeImpl<
-    IsCastable<NullptrToIgnore<std::remove_pointer_t<CASE_RETURN_TYPES>>...>,
+    utils::IsCastable<NullptrToIgnore<std::remove_pointer_t<CASE_RETURN_TYPES>>...>,
     REQUESTED_TYPE,
     CASE_RETURN_TYPES...>::type;
 
@@ -162,7 +162,9 @@
 /// @param cases the switch cases
 /// @return the value returned by the called case. If no cases matched, then the zero value for the
 /// consistent case type.
-template <typename RETURN_TYPE = detail::Infer, typename T = CastableBase, typename... CASES>
+template <typename RETURN_TYPE = utils::detail::Infer,
+          typename T = utils::CastableBase,
+          typename... CASES>
 inline auto Switch(T* object, CASES&&... cases) {
     using ReturnType = detail::SwitchReturnType<RETURN_TYPE, utils::traits::ReturnType<CASES>...>;
     static constexpr int kDefaultIndex = detail::IndexOfDefaultCase<std::tuple<CASES...>>();
@@ -203,7 +205,7 @@
     ReturnStorage storage;
     auto* result = utils::Bitcast<ReturnTypeOrU8*>(&storage);
 
-    const TypeInfo& type_info = object->TypeInfo();
+    const utils::TypeInfo& type_info = object->TypeInfo();
 
     // Examines the parameter type of the case function.
     // If the parameter is a pointer type that `object` is of, or derives from, then that case
diff --git a/src/tint/switch_bench.cc b/src/tint/switch_bench.cc
index ea6098b..e73d982 100644
--- a/src/tint/switch_bench.cc
+++ b/src/tint/switch_bench.cc
@@ -21,46 +21,46 @@
 namespace tint {
 namespace {
 
-struct Base : public tint::Castable<Base> {};
-struct A : public tint::Castable<A, Base> {};
-struct AA : public tint::Castable<AA, A> {};
-struct AAA : public tint::Castable<AAA, AA> {};
-struct AAB : public tint::Castable<AAB, AA> {};
-struct AAC : public tint::Castable<AAC, AA> {};
-struct AB : public tint::Castable<AB, A> {};
-struct ABA : public tint::Castable<ABA, AB> {};
-struct ABB : public tint::Castable<ABB, AB> {};
-struct ABC : public tint::Castable<ABC, AB> {};
-struct AC : public tint::Castable<AC, A> {};
-struct ACA : public tint::Castable<ACA, AC> {};
-struct ACB : public tint::Castable<ACB, AC> {};
-struct ACC : public tint::Castable<ACC, AC> {};
-struct B : public tint::Castable<B, Base> {};
-struct BA : public tint::Castable<BA, B> {};
-struct BAA : public tint::Castable<BAA, BA> {};
-struct BAB : public tint::Castable<BAB, BA> {};
-struct BAC : public tint::Castable<BAC, BA> {};
-struct BB : public tint::Castable<BB, B> {};
-struct BBA : public tint::Castable<BBA, BB> {};
-struct BBB : public tint::Castable<BBB, BB> {};
-struct BBC : public tint::Castable<BBC, BB> {};
-struct BC : public tint::Castable<BC, B> {};
-struct BCA : public tint::Castable<BCA, BC> {};
-struct BCB : public tint::Castable<BCB, BC> {};
-struct BCC : public tint::Castable<BCC, BC> {};
-struct C : public tint::Castable<C, Base> {};
-struct CA : public tint::Castable<CA, C> {};
-struct CAA : public tint::Castable<CAA, CA> {};
-struct CAB : public tint::Castable<CAB, CA> {};
-struct CAC : public tint::Castable<CAC, CA> {};
-struct CB : public tint::Castable<CB, C> {};
-struct CBA : public tint::Castable<CBA, CB> {};
-struct CBB : public tint::Castable<CBB, CB> {};
-struct CBC : public tint::Castable<CBC, CB> {};
-struct CC : public tint::Castable<CC, C> {};
-struct CCA : public tint::Castable<CCA, CC> {};
-struct CCB : public tint::Castable<CCB, CC> {};
-struct CCC : public tint::Castable<CCC, CC> {};
+struct Base : public tint::utils::Castable<Base> {};
+struct A : public tint::utils::Castable<A, Base> {};
+struct AA : public tint::utils::Castable<AA, A> {};
+struct AAA : public tint::utils::Castable<AAA, AA> {};
+struct AAB : public tint::utils::Castable<AAB, AA> {};
+struct AAC : public tint::utils::Castable<AAC, AA> {};
+struct AB : public tint::utils::Castable<AB, A> {};
+struct ABA : public tint::utils::Castable<ABA, AB> {};
+struct ABB : public tint::utils::Castable<ABB, AB> {};
+struct ABC : public tint::utils::Castable<ABC, AB> {};
+struct AC : public tint::utils::Castable<AC, A> {};
+struct ACA : public tint::utils::Castable<ACA, AC> {};
+struct ACB : public tint::utils::Castable<ACB, AC> {};
+struct ACC : public tint::utils::Castable<ACC, AC> {};
+struct B : public tint::utils::Castable<B, Base> {};
+struct BA : public tint::utils::Castable<BA, B> {};
+struct BAA : public tint::utils::Castable<BAA, BA> {};
+struct BAB : public tint::utils::Castable<BAB, BA> {};
+struct BAC : public tint::utils::Castable<BAC, BA> {};
+struct BB : public tint::utils::Castable<BB, B> {};
+struct BBA : public tint::utils::Castable<BBA, BB> {};
+struct BBB : public tint::utils::Castable<BBB, BB> {};
+struct BBC : public tint::utils::Castable<BBC, BB> {};
+struct BC : public tint::utils::Castable<BC, B> {};
+struct BCA : public tint::utils::Castable<BCA, BC> {};
+struct BCB : public tint::utils::Castable<BCB, BC> {};
+struct BCC : public tint::utils::Castable<BCC, BC> {};
+struct C : public tint::utils::Castable<C, Base> {};
+struct CA : public tint::utils::Castable<CA, C> {};
+struct CAA : public tint::utils::Castable<CAA, CA> {};
+struct CAB : public tint::utils::Castable<CAB, CA> {};
+struct CAC : public tint::utils::Castable<CAC, CA> {};
+struct CB : public tint::utils::Castable<CB, C> {};
+struct CBA : public tint::utils::Castable<CBA, CB> {};
+struct CBB : public tint::utils::Castable<CBB, CB> {};
+struct CBC : public tint::utils::Castable<CBC, CB> {};
+struct CC : public tint::utils::Castable<CC, C> {};
+struct CCA : public tint::utils::Castable<CCA, CC> {};
+struct CCB : public tint::utils::Castable<CCB, CC> {};
+struct CCC : public tint::utils::Castable<CCC, CC> {};
 
 using AllTypes = std::tuple<Base,
                             A,
diff --git a/src/tint/switch_test.cc b/src/tint/switch_test.cc
index d93f830..f35192e 100644
--- a/src/tint/switch_test.cc
+++ b/src/tint/switch_test.cc
@@ -22,15 +22,15 @@
 namespace tint {
 namespace {
 
-struct Animal : public tint::Castable<Animal> {};
-struct Amphibian : public tint::Castable<Amphibian, Animal> {};
-struct Mammal : public tint::Castable<Mammal, Animal> {};
-struct Reptile : public tint::Castable<Reptile, Animal> {};
-struct Frog : public tint::Castable<Frog, Amphibian> {};
-struct Bear : public tint::Castable<Bear, Mammal> {};
-struct Lizard : public tint::Castable<Lizard, Reptile> {};
-struct Gecko : public tint::Castable<Gecko, Lizard> {};
-struct Iguana : public tint::Castable<Iguana, Lizard> {};
+struct Animal : public tint::utils::Castable<Animal> {};
+struct Amphibian : public tint::utils::Castable<Amphibian, Animal> {};
+struct Mammal : public tint::utils::Castable<Mammal, Animal> {};
+struct Reptile : public tint::utils::Castable<Reptile, Animal> {};
+struct Frog : public tint::utils::Castable<Frog, Amphibian> {};
+struct Bear : public tint::utils::Castable<Bear, Mammal> {};
+struct Lizard : public tint::utils::Castable<Lizard, Reptile> {};
+struct Gecko : public tint::utils::Castable<Gecko, Lizard> {};
+struct Iguana : public tint::utils::Castable<Iguana, Lizard> {};
 
 TEST(Castable, SwitchNoDefault) {
     std::unique_ptr<Animal> frog = std::make_unique<Frog>();
@@ -331,8 +331,8 @@
             gecko.get(),                     //
             [](Mammal* p) { return p; },     //
             [](Amphibian* p) { return p; },  //
-            [](Default) -> CastableBase* { return nullptr; });
-        static_assert(std::is_same_v<decltype(result), CastableBase*>);
+            [](Default) -> utils::CastableBase* { return nullptr; });
+        static_assert(std::is_same_v<decltype(result), utils::CastableBase*>);
         EXPECT_EQ(result, nullptr);
     }
 }
@@ -444,12 +444,12 @@
         EXPECT_EQ(result, nullptr);
     }
     {
-        auto* result = Switch<CastableBase>(
+        auto* result = Switch<utils::CastableBase>(
             bear.get(),                   //
             [](Mammal* p) { return p; },  //
             [](Amphibian* p) { return const_cast<const Amphibian*>(p); },
             [](Default) { return nullptr; });
-        static_assert(std::is_same_v<decltype(result), const CastableBase*>);
+        static_assert(std::is_same_v<decltype(result), const utils::CastableBase*>);
         EXPECT_EQ(result, bear.get());
     }
     {
@@ -476,11 +476,11 @@
         EXPECT_EQ(result, nullptr);
     }
     {
-        auto* result = Switch<CastableBase>(
+        auto* result = Switch<utils::CastableBase>(
             bear.get(),                                                     //
             [](Mammal* p) { return p; },                                    //
             [](Amphibian* p) { return const_cast<const Amphibian*>(p); });  //
-        static_assert(std::is_same_v<decltype(result), const CastableBase*>);
+        static_assert(std::is_same_v<decltype(result), const utils::CastableBase*>);
         EXPECT_EQ(result, bear.get());
     }
     {
diff --git a/src/tint/transform/add_block_attribute.h b/src/tint/transform/add_block_attribute.h
index d5e8c4e..3eeb148 100644
--- a/src/tint/transform/add_block_attribute.h
+++ b/src/tint/transform/add_block_attribute.h
@@ -24,11 +24,11 @@
 
 /// AddBlockAttribute is a transform that wrap the store type of a buffer into a struct if possible,
 /// then adds an `@internal(block)` attribute to the wrapper struct.
-class AddBlockAttribute final : public Castable<AddBlockAttribute, Transform> {
+class AddBlockAttribute final : public utils::Castable<AddBlockAttribute, Transform> {
   public:
     /// BlockAttribute is an InternalAttribute that is used to decorate a
     // structure that is used as a buffer in SPIR-V or GLSL.
-    class BlockAttribute final : public Castable<BlockAttribute, ast::InternalAttribute> {
+    class BlockAttribute final : public utils::Castable<BlockAttribute, ast::InternalAttribute> {
       public:
         /// Constructor
         /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/transform/add_empty_entry_point.h b/src/tint/transform/add_empty_entry_point.h
index 828f3b5..709b574 100644
--- a/src/tint/transform/add_empty_entry_point.h
+++ b/src/tint/transform/add_empty_entry_point.h
@@ -20,7 +20,7 @@
 namespace tint::transform {
 
 /// Add an empty entry point to the module, if no other entry points exist.
-class AddEmptyEntryPoint final : public Castable<AddEmptyEntryPoint, Transform> {
+class AddEmptyEntryPoint final : public utils::Castable<AddEmptyEntryPoint, Transform> {
   public:
     /// Constructor
     AddEmptyEntryPoint();
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index e0358b4..98d4a6b 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -65,9 +65,10 @@
     ApplyResult Run() {
         auto* cfg = inputs.Get<Config>();
         if (cfg == nullptr) {
-            b.Diagnostics().add_error(diag::System::Transform,
-                                      "missing transform data for " +
-                                          std::string(TypeInfo::Of<ArrayLengthFromUniform>().name));
+            b.Diagnostics().add_error(
+                diag::System::Transform,
+                "missing transform data for " +
+                    std::string(utils::TypeInfo::Of<ArrayLengthFromUniform>().name));
             return Program(std::move(b));
         }
 
diff --git a/src/tint/transform/array_length_from_uniform.h b/src/tint/transform/array_length_from_uniform.h
index 507ea37..bb50dc0 100644
--- a/src/tint/transform/array_length_from_uniform.h
+++ b/src/tint/transform/array_length_from_uniform.h
@@ -52,7 +52,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
-class ArrayLengthFromUniform final : public Castable<ArrayLengthFromUniform, Transform> {
+class ArrayLengthFromUniform final : public utils::Castable<ArrayLengthFromUniform, Transform> {
   public:
     /// Constructor
     ArrayLengthFromUniform();
@@ -60,7 +60,7 @@
     ~ArrayLengthFromUniform() override;
 
     /// Configuration options for the ArrayLengthFromUniform transform.
-    struct Config final : public Castable<Data, transform::Data> {
+    struct Config final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param ubo_bp the binding point to use for the generated uniform buffer.
         explicit Config(sem::BindingPoint ubo_bp);
@@ -85,7 +85,7 @@
     /// Information produced about what the transform did.
     /// If there were no calls to the arrayLength() builtin, then no Result will
     /// be emitted.
-    struct Result final : public Castable<Result, transform::Data> {
+    struct Result final : public utils::Castable<Result, transform::Data> {
         /// Constructor
         /// @param used_size_indices Indices into the UBO that are statically used.
         explicit Result(std::unordered_set<uint32_t> used_size_indices);
diff --git a/src/tint/transform/binding_remapper.h b/src/tint/transform/binding_remapper.h
index 8fdba2a..9eaedad 100644
--- a/src/tint/transform/binding_remapper.h
+++ b/src/tint/transform/binding_remapper.h
@@ -28,7 +28,7 @@
 
 /// BindingRemapper is a transform used to remap resource binding points and
 /// access controls.
-class BindingRemapper final : public Castable<BindingRemapper, Transform> {
+class BindingRemapper final : public utils::Castable<BindingRemapper, Transform> {
   public:
     /// BindingPoints is a map of old binding point to new binding point
     using BindingPoints = std::unordered_map<BindingPoint, BindingPoint>;
@@ -38,7 +38,7 @@
 
     /// Remappings is consumed by the BindingRemapper transform.
     /// Data holds information about shader usage and constant buffer offsets.
-    struct Remappings final : public Castable<Data, transform::Data> {
+    struct Remappings final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param bp a map of new binding points
         /// @param ac a map of new access controls
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index cc770ab..9cbb65f 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -1251,7 +1251,8 @@
                     auto* src_ty = conv->Source();
                     if (tint::Is<type::F32>(type::Type::ElementOf(src_ty))) {
                         auto* dst_ty = conv->Target();
-                        if (tint::IsAnyOf<type::I32, type::U32>(type::Type::ElementOf(dst_ty))) {
+                        if (tint::utils::IsAnyOf<type::I32, type::U32>(
+                                type::Type::ElementOf(dst_ty))) {
                             return f32_conv_polyfills.GetOrCreate(dst_ty, [&] {  //
                                 return ConvF32ToIU32(src_ty, dst_ty);
                             });
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index dee0bec..e42ba8f 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -20,7 +20,7 @@
 namespace tint::transform {
 
 /// Implements builtins for backends that do not have a native implementation.
-class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
+class BuiltinPolyfill final : public utils::Castable<BuiltinPolyfill, Transform> {
   public:
     /// Constructor
     BuiltinPolyfill();
@@ -89,7 +89,7 @@
 
     /// Config is consumed by the BuiltinPolyfill transform.
     /// Config specifies the builtins that should be polyfilled.
-    struct Config final : public Castable<Data, transform::Data> {
+    struct Config final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param b the list of builtins to polyfill
         explicit Config(const Builtins& b);
diff --git a/src/tint/transform/calculate_array_length.h b/src/tint/transform/calculate_array_length.h
index e5714a8..6ebb65c 100644
--- a/src/tint/transform/calculate_array_length.h
+++ b/src/tint/transform/calculate_array_length.h
@@ -32,11 +32,12 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
-class CalculateArrayLength final : public Castable<CalculateArrayLength, Transform> {
+class CalculateArrayLength final : public utils::Castable<CalculateArrayLength, Transform> {
   public:
     /// BufferSizeIntrinsic is an InternalAttribute that's applied to intrinsic
     /// functions used to obtain the runtime size of a storage buffer.
-    class BufferSizeIntrinsic final : public Castable<BufferSizeIntrinsic, ast::InternalAttribute> {
+    class BufferSizeIntrinsic final
+        : public utils::Castable<BufferSizeIntrinsic, ast::InternalAttribute> {
       public:
         /// Constructor
         /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/transform/canonicalize_entry_point_io.h b/src/tint/transform/canonicalize_entry_point_io.h
index fbfed5e..85e9b22 100644
--- a/src/tint/transform/canonicalize_entry_point_io.h
+++ b/src/tint/transform/canonicalize_entry_point_io.h
@@ -82,7 +82,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * Unshadow
-class CanonicalizeEntryPointIO final : public Castable<CanonicalizeEntryPointIO, Transform> {
+class CanonicalizeEntryPointIO final : public utils::Castable<CanonicalizeEntryPointIO, Transform> {
   public:
     /// ShaderStyle is an enumerator of different ways to emit shader IO.
     enum class ShaderStyle {
@@ -97,7 +97,7 @@
     };
 
     /// Configuration options for the transform.
-    struct Config final : public Castable<Config, Data> {
+    struct Config final : public utils::Castable<Config, Data> {
         /// Constructor
         /// @param style the approach to use for emitting shader IO.
         /// @param sample_mask an optional sample mask to combine with shader masks
diff --git a/src/tint/transform/clamp_frag_depth.h b/src/tint/transform/clamp_frag_depth.h
index 3e3f168..88a7ee1 100644
--- a/src/tint/transform/clamp_frag_depth.h
+++ b/src/tint/transform/clamp_frag_depth.h
@@ -54,7 +54,7 @@
 ///     return clamp_frag_depth(0.0);
 ///   }
 /// ```
-class ClampFragDepth final : public Castable<ClampFragDepth, Transform> {
+class ClampFragDepth final : public utils::Castable<ClampFragDepth, Transform> {
   public:
     /// Constructor
     ClampFragDepth();
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index 3e9aa7f..ebf8126 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -158,7 +158,7 @@
         for (auto* global : ctx.src->AST().GlobalVariables()) {
             auto* global_sem = sem.Get(global)->As<sem::GlobalVariable>();
             auto* type = ctx.src->TypeOf(global->type);
-            if (tint::IsAnyOf<type::Texture, type::Sampler>(type) &&
+            if (tint::utils::IsAnyOf<type::Texture, type::Sampler>(type) &&
                 !type->Is<type::StorageTexture>()) {
                 ctx.Remove(ctx.src->AST().GlobalDeclarations(), global);
             } else if (auto binding_point = global_sem->BindingPoint()) {
diff --git a/src/tint/transform/combine_samplers.h b/src/tint/transform/combine_samplers.h
index 6834abe..b521a55 100644
--- a/src/tint/transform/combine_samplers.h
+++ b/src/tint/transform/combine_samplers.h
@@ -52,7 +52,7 @@
 /// information needed to represent a combined sampler in GLSL
 /// (dimensionality, component type, etc). The GLSL writer outputs such
 /// (Tint) Textures as (GLSL) Samplers.
-class CombineSamplers final : public Castable<CombineSamplers, Transform> {
+class CombineSamplers final : public utils::Castable<CombineSamplers, Transform> {
   public:
     /// A pair of binding points.
     using SamplerTexturePair = sem::SamplerTexturePair;
@@ -62,7 +62,7 @@
 
     /// The client-provided mapping from separate texture and sampler binding
     /// points to combined sampler binding point.
-    struct BindingInfo final : public Castable<Data, transform::Data> {
+    struct BindingInfo final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param map the map of all (texture, sampler) -> (combined) pairs
         /// @param placeholder the binding point to use for placeholder samplers.
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index fb8dc62..564451c 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -62,7 +62,7 @@
 
 /// Offset is a simple ast::Expression builder interface, used to build byte
 /// offsets for storage and uniform buffer accesses.
-struct Offset : Castable<Offset> {
+struct Offset : utils::Castable<Offset> {
     /// @returns builds and returns the ast::Expression in `ctx.dst`
     virtual const ast::Expression* Build(CloneContext& ctx) const = 0;
 };
@@ -86,7 +86,7 @@
 
 /// OffsetLiteral is an implementation of Offset that constructs a u32 literal
 /// value.
-struct OffsetLiteral final : Castable<OffsetLiteral, Offset> {
+struct OffsetLiteral final : utils::Castable<OffsetLiteral, Offset> {
     uint32_t const literal = 0;
 
     explicit OffsetLiteral(uint32_t lit) : literal(lit) {}
diff --git a/src/tint/transform/decompose_memory_access.h b/src/tint/transform/decompose_memory_access.h
index f85ad6d..8f06726 100644
--- a/src/tint/transform/decompose_memory_access.h
+++ b/src/tint/transform/decompose_memory_access.h
@@ -29,13 +29,13 @@
 
 /// DecomposeMemoryAccess is a transform used to replace storage and uniform buffer accesses with a
 /// combination of load, store or atomic functions on primitive types.
-class DecomposeMemoryAccess final : public Castable<DecomposeMemoryAccess, Transform> {
+class DecomposeMemoryAccess final : public utils::Castable<DecomposeMemoryAccess, Transform> {
   public:
     /// Intrinsic is an InternalAttribute that's used to decorate a stub function so that the HLSL
     /// transforms this into calls to
     /// `[RW]ByteAddressBuffer.Load[N]()` or `[RW]ByteAddressBuffer.Store[N]()`,
     /// with a possible cast.
-    class Intrinsic final : public Castable<Intrinsic, ast::InternalAttribute> {
+    class Intrinsic final : public utils::Castable<Intrinsic, ast::InternalAttribute> {
       public:
         /// Intrinsic op
         enum class Op {
diff --git a/src/tint/transform/decompose_strided_array.h b/src/tint/transform/decompose_strided_array.h
index 9555a9a..aee75b0 100644
--- a/src/tint/transform/decompose_strided_array.h
+++ b/src/tint/transform/decompose_strided_array.h
@@ -27,7 +27,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
-class DecomposeStridedArray final : public Castable<DecomposeStridedArray, Transform> {
+class DecomposeStridedArray final : public utils::Castable<DecomposeStridedArray, Transform> {
   public:
     /// Constructor
     DecomposeStridedArray();
diff --git a/src/tint/transform/decompose_strided_matrix.h b/src/tint/transform/decompose_strided_matrix.h
index 947dfc6..8fa0feb 100644
--- a/src/tint/transform/decompose_strided_matrix.h
+++ b/src/tint/transform/decompose_strided_matrix.h
@@ -27,7 +27,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
-class DecomposeStridedMatrix final : public Castable<DecomposeStridedMatrix, Transform> {
+class DecomposeStridedMatrix final : public utils::Castable<DecomposeStridedMatrix, Transform> {
   public:
     /// Constructor
     DecomposeStridedMatrix();
diff --git a/src/tint/transform/demote_to_helper.h b/src/tint/transform/demote_to_helper.h
index ba87feb..75909d2 100644
--- a/src/tint/transform/demote_to_helper.h
+++ b/src/tint/transform/demote_to_helper.h
@@ -29,7 +29,7 @@
 /// @note Depends on the following transforms to have been run first:
 /// * PromoteSideEffectsToDecl
 /// * ExpandCompoundAssignment
-class DemoteToHelper final : public Castable<DemoteToHelper, Transform> {
+class DemoteToHelper final : public utils::Castable<DemoteToHelper, Transform> {
   public:
     /// Constructor
     DemoteToHelper();
diff --git a/src/tint/transform/direct_variable_access.h b/src/tint/transform/direct_variable_access.h
index 34b6e91..74aafa7 100644
--- a/src/tint/transform/direct_variable_access.h
+++ b/src/tint/transform/direct_variable_access.h
@@ -32,7 +32,7 @@
 /// comments in src/tint/transform/direct_variable_access.cc.
 ///
 /// @note DirectVariableAccess requires the transform::Unshadow transform to have been run first.
-class DirectVariableAccess final : public Castable<DirectVariableAccess, Transform> {
+class DirectVariableAccess final : public utils::Castable<DirectVariableAccess, Transform> {
   public:
     /// Constructor
     DirectVariableAccess();
@@ -49,7 +49,7 @@
 
     /// Config is consumed by the DirectVariableAccess transform.
     /// Config specifies the behavior of the transform.
-    struct Config final : public Castable<Data, transform::Data> {
+    struct Config final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param options behavior of the transform
         explicit Config(const Options& options);
diff --git a/src/tint/transform/disable_uniformity_analysis.h b/src/tint/transform/disable_uniformity_analysis.h
index a9922af..a2827c5 100644
--- a/src/tint/transform/disable_uniformity_analysis.h
+++ b/src/tint/transform/disable_uniformity_analysis.h
@@ -20,7 +20,8 @@
 namespace tint::transform {
 
 /// Disable uniformity analysis for the program.
-class DisableUniformityAnalysis final : public Castable<DisableUniformityAnalysis, Transform> {
+class DisableUniformityAnalysis final
+    : public utils::Castable<DisableUniformityAnalysis, Transform> {
   public:
     /// Constructor
     DisableUniformityAnalysis();
diff --git a/src/tint/transform/expand_compound_assignment.h b/src/tint/transform/expand_compound_assignment.h
index 6b299c5..ce5a36c 100644
--- a/src/tint/transform/expand_compound_assignment.h
+++ b/src/tint/transform/expand_compound_assignment.h
@@ -38,7 +38,7 @@
 ///
 /// This transform also handles increment and decrement statements in the same
 /// manner, by replacing `i++` with `i = i + 1`.
-class ExpandCompoundAssignment final : public Castable<ExpandCompoundAssignment, Transform> {
+class ExpandCompoundAssignment final : public utils::Castable<ExpandCompoundAssignment, Transform> {
   public:
     /// Constructor
     ExpandCompoundAssignment();
diff --git a/src/tint/transform/first_index_offset.h b/src/tint/transform/first_index_offset.h
index f84d811..dbf47ae 100644
--- a/src/tint/transform/first_index_offset.h
+++ b/src/tint/transform/first_index_offset.h
@@ -57,12 +57,12 @@
 ///   }
 /// ```
 ///
-class FirstIndexOffset final : public Castable<FirstIndexOffset, Transform> {
+class FirstIndexOffset final : public utils::Castable<FirstIndexOffset, Transform> {
   public:
     /// BindingPoint is consumed by the FirstIndexOffset transform.
     /// BindingPoint specifies the binding point of the first index uniform
     /// buffer.
-    struct BindingPoint final : public Castable<BindingPoint, transform::Data> {
+    struct BindingPoint final : public utils::Castable<BindingPoint, transform::Data> {
         /// Constructor
         BindingPoint();
 
@@ -82,7 +82,7 @@
 
     /// Data is outputted by the FirstIndexOffset transform.
     /// Data holds information about shader usage and constant buffer offsets.
-    struct Data final : public Castable<Data, transform::Data> {
+    struct Data final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param has_vtx_or_inst_index True if the shader uses vertex_index or
         /// instance_index
diff --git a/src/tint/transform/for_loop_to_loop.h b/src/tint/transform/for_loop_to_loop.h
index fe3db97..8ed53c5 100644
--- a/src/tint/transform/for_loop_to_loop.h
+++ b/src/tint/transform/for_loop_to_loop.h
@@ -21,7 +21,7 @@
 
 /// ForLoopToLoop is a Transform that converts a for-loop statement into a loop
 /// statement. This is required by the SPIR-V writer.
-class ForLoopToLoop final : public Castable<ForLoopToLoop, Transform> {
+class ForLoopToLoop final : public utils::Castable<ForLoopToLoop, Transform> {
   public:
     /// Constructor
     ForLoopToLoop();
diff --git a/src/tint/transform/localize_struct_array_assignment.h b/src/tint/transform/localize_struct_array_assignment.h
index 169e33c..8173fa8 100644
--- a/src/tint/transform/localize_struct_array_assignment.h
+++ b/src/tint/transform/localize_struct_array_assignment.h
@@ -28,7 +28,7 @@
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
 class LocalizeStructArrayAssignment final
-    : public Castable<LocalizeStructArrayAssignment, Transform> {
+    : public utils::Castable<LocalizeStructArrayAssignment, Transform> {
   public:
     /// Constructor
     LocalizeStructArrayAssignment();
diff --git a/src/tint/transform/manager.h b/src/tint/transform/manager.h
index 64ca847..a364c1a 100644
--- a/src/tint/transform/manager.h
+++ b/src/tint/transform/manager.h
@@ -27,7 +27,7 @@
 /// The inner transforms will execute in the appended order.
 /// If any inner transform fails the manager will return immediately and
 /// the error can be retrieved with the Output's diagnostics.
-class Manager final : public Castable<Manager, Transform> {
+class Manager final : public utils::Castable<Manager, Transform> {
   public:
     /// Constructor
     Manager();
diff --git a/src/tint/transform/merge_return.h b/src/tint/transform/merge_return.h
index f6db5c2..fa9654b 100644
--- a/src/tint/transform/merge_return.h
+++ b/src/tint/transform/merge_return.h
@@ -20,7 +20,7 @@
 namespace tint::transform {
 
 /// Merge return statements into a single return at the end of the function.
-class MergeReturn final : public Castable<MergeReturn, Transform> {
+class MergeReturn final : public utils::Castable<MergeReturn, Transform> {
   public:
     /// Constructor
     MergeReturn();
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.h b/src/tint/transform/module_scope_var_to_entry_point_param.h
index 377151f..4f4f562 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.h
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.h
@@ -62,7 +62,7 @@
 /// }
 /// ```
 class ModuleScopeVarToEntryPointParam final
-    : public Castable<ModuleScopeVarToEntryPointParam, Transform> {
+    : public utils::Castable<ModuleScopeVarToEntryPointParam, Transform> {
   public:
     /// Constructor
     ModuleScopeVarToEntryPointParam();
diff --git a/src/tint/transform/multiplanar_external_texture.h b/src/tint/transform/multiplanar_external_texture.h
index 656d0ad..c47c345 100644
--- a/src/tint/transform/multiplanar_external_texture.h
+++ b/src/tint/transform/multiplanar_external_texture.h
@@ -37,7 +37,8 @@
 /// decoding, gamut conversion, and gamma encoding steps. Specifically
 // for BT.709 to SRGB conversion, it takes the fast path only doing the yuv->rgb
 // step and skipping all other steps.
-class MultiplanarExternalTexture final : public Castable<MultiplanarExternalTexture, Transform> {
+class MultiplanarExternalTexture final
+    : public utils::Castable<MultiplanarExternalTexture, Transform> {
   public:
     /// This struct identifies the binding groups and locations for new bindings to
     /// use when transforming a texture_external instance.
@@ -51,7 +52,7 @@
     /// NewBindingPoints is consumed by the MultiplanarExternalTexture transform.
     /// Data holds information about location of each texture_external binding and
     /// which binding slots it should expand into.
-    struct NewBindingPoints final : public Castable<Data, transform::Data> {
+    struct NewBindingPoints final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param bm a map to the new binding slots to use.
         explicit NewBindingPoints(BindingsMap bm);
diff --git a/src/tint/transform/num_workgroups_from_uniform.h b/src/tint/transform/num_workgroups_from_uniform.h
index 25308f2..049c1db 100644
--- a/src/tint/transform/num_workgroups_from_uniform.h
+++ b/src/tint/transform/num_workgroups_from_uniform.h
@@ -44,7 +44,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * CanonicalizeEntryPointIO
-class NumWorkgroupsFromUniform final : public Castable<NumWorkgroupsFromUniform, Transform> {
+class NumWorkgroupsFromUniform final : public utils::Castable<NumWorkgroupsFromUniform, Transform> {
   public:
     /// Constructor
     NumWorkgroupsFromUniform();
@@ -52,7 +52,7 @@
     ~NumWorkgroupsFromUniform() override;
 
     /// Configuration options for the NumWorkgroupsFromUniform transform.
-    struct Config final : public Castable<Data, transform::Data> {
+    struct Config final : public utils::Castable<Data, transform::Data> {
         /// Constructor
         /// @param ubo_bp the binding point to use for the generated uniform buffer. If ubo_bp
         /// contains no value, a free binding point will be used to ensure the generated program is
diff --git a/src/tint/transform/packed_vec3.h b/src/tint/transform/packed_vec3.h
index 89e8286..d13111b 100644
--- a/src/tint/transform/packed_vec3.h
+++ b/src/tint/transform/packed_vec3.h
@@ -38,7 +38,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * ExpandCompoundAssignment
-class PackedVec3 final : public Castable<PackedVec3, Transform> {
+class PackedVec3 final : public utils::Castable<PackedVec3, Transform> {
   public:
     /// Constructor
     PackedVec3();
diff --git a/src/tint/transform/pad_structs.h b/src/tint/transform/pad_structs.h
index 1add1d6..f5bf8cc 100644
--- a/src/tint/transform/pad_structs.h
+++ b/src/tint/transform/pad_structs.h
@@ -24,7 +24,7 @@
 /// the offset= decoration.
 ///
 /// @note This transform requires the CanonicalizeEntryPointIO transform to have been run first.
-class PadStructs final : public Castable<PadStructs, Transform> {
+class PadStructs final : public utils::Castable<PadStructs, Transform> {
   public:
     /// Constructor
     PadStructs();
diff --git a/src/tint/transform/preserve_padding.h b/src/tint/transform/preserve_padding.h
index 3bf0a35..e025c2e 100644
--- a/src/tint/transform/preserve_padding.h
+++ b/src/tint/transform/preserve_padding.h
@@ -26,7 +26,7 @@
 /// assignments into element-wise assignments via helper functions.
 ///
 /// @note Assumes that the DirectVariableTransform will be run afterwards for backends that need it.
-class PreservePadding final : public Castable<PreservePadding, Transform> {
+class PreservePadding final : public utils::Castable<PreservePadding, Transform> {
   public:
     /// Constructor
     PreservePadding();
diff --git a/src/tint/transform/promote_initializers_to_let.h b/src/tint/transform/promote_initializers_to_let.h
index b1bb291..a08ad36 100644
--- a/src/tint/transform/promote_initializers_to_let.h
+++ b/src/tint/transform/promote_initializers_to_let.h
@@ -25,7 +25,7 @@
 /// array or structure. For example, the following is not immediately expressable for HLSL:
 ///   `array<i32, 2>(1, 2)[0]`
 /// @see crbug.com/tint/406
-class PromoteInitializersToLet final : public Castable<PromoteInitializersToLet, Transform> {
+class PromoteInitializersToLet final : public utils::Castable<PromoteInitializersToLet, Transform> {
   public:
     /// Constructor
     PromoteInitializersToLet();
diff --git a/src/tint/transform/promote_side_effects_to_decl.cc b/src/tint/transform/promote_side_effects_to_decl.cc
index 9db7e38..fef15ee 100644
--- a/src/tint/transform/promote_side_effects_to_decl.cc
+++ b/src/tint/transform/promote_side_effects_to_decl.cc
@@ -52,7 +52,7 @@
 // This first transform converts side-effecting for-loops to loops and else-ifs
 // to else {if}s so that the next transform, DecomposeSideEffects, can insert
 // hoisted expressions above their current location.
-struct SimplifySideEffectStatements : Castable<PromoteSideEffectsToDecl, Transform> {
+struct SimplifySideEffectStatements : tint::utils::Castable<PromoteSideEffectsToDecl, Transform> {
     ApplyResult Apply(const Program* src, const DataMap& inputs, DataMap& outputs) const override;
 };
 
@@ -87,7 +87,7 @@
 // Decomposes side-effecting expressions to ensure order of evaluation. This
 // handles both breaking down logical binary expressions for short-circuit
 // evaluation, as well as hoisting expressions to ensure order of evaluation.
-struct DecomposeSideEffects : Castable<PromoteSideEffectsToDecl, Transform> {
+struct DecomposeSideEffects : tint::utils::Castable<PromoteSideEffectsToDecl, Transform> {
     class CollectHoistsState;
     class DecomposeState;
     ApplyResult Apply(const Program* src, const DataMap& inputs, DataMap& outputs) const override;
diff --git a/src/tint/transform/promote_side_effects_to_decl.h b/src/tint/transform/promote_side_effects_to_decl.h
index 99e80c6..36426b6 100644
--- a/src/tint/transform/promote_side_effects_to_decl.h
+++ b/src/tint/transform/promote_side_effects_to_decl.h
@@ -23,7 +23,7 @@
 /// declarations before the statement of usage with the goal of ensuring
 /// left-to-right order of evaluation, while respecting short-circuit
 /// evaluation.
-class PromoteSideEffectsToDecl final : public Castable<PromoteSideEffectsToDecl, Transform> {
+class PromoteSideEffectsToDecl final : public utils::Castable<PromoteSideEffectsToDecl, Transform> {
   public:
     /// Constructor
     PromoteSideEffectsToDecl();
diff --git a/src/tint/transform/remove_continue_in_switch.h b/src/tint/transform/remove_continue_in_switch.h
index 1070906..a888513 100644
--- a/src/tint/transform/remove_continue_in_switch.h
+++ b/src/tint/transform/remove_continue_in_switch.h
@@ -23,7 +23,7 @@
 /// bool variable, and checking if the variable is set after the switch to
 /// continue. It is necessary to work around FXC "error X3708: continue cannot
 /// be used in a switch". See crbug.com/tint/1080.
-class RemoveContinueInSwitch final : public Castable<RemoveContinueInSwitch, Transform> {
+class RemoveContinueInSwitch final : public utils::Castable<RemoveContinueInSwitch, Transform> {
   public:
     /// Constructor
     RemoveContinueInSwitch();
diff --git a/src/tint/transform/remove_phonies.h b/src/tint/transform/remove_phonies.h
index 99a049e..7b1f9fd 100644
--- a/src/tint/transform/remove_phonies.h
+++ b/src/tint/transform/remove_phonies.h
@@ -25,7 +25,7 @@
 /// RemovePhonies is a Transform that removes all phony-assignment statements,
 /// while preserving function call expressions in the RHS of the assignment that
 /// may have side-effects. It also removes calls to builtins that return a constant value.
-class RemovePhonies final : public Castable<RemovePhonies, Transform> {
+class RemovePhonies final : public utils::Castable<RemovePhonies, Transform> {
   public:
     /// Constructor
     RemovePhonies();
diff --git a/src/tint/transform/remove_unreachable_statements.h b/src/tint/transform/remove_unreachable_statements.h
index f5848f5..dd1054d 100644
--- a/src/tint/transform/remove_unreachable_statements.h
+++ b/src/tint/transform/remove_unreachable_statements.h
@@ -24,7 +24,8 @@
 
 /// RemoveUnreachableStatements is a Transform that removes all statements
 /// marked as unreachable.
-class RemoveUnreachableStatements final : public Castable<RemoveUnreachableStatements, Transform> {
+class RemoveUnreachableStatements final
+    : public utils::Castable<RemoveUnreachableStatements, Transform> {
   public:
     /// Constructor
     RemoveUnreachableStatements();
diff --git a/src/tint/transform/renamer.h b/src/tint/transform/renamer.h
index 8a9f97e..015e386 100644
--- a/src/tint/transform/renamer.h
+++ b/src/tint/transform/renamer.h
@@ -23,11 +23,11 @@
 namespace tint::transform {
 
 /// Renamer is a Transform that renames all the symbols in a program.
-class Renamer final : public Castable<Renamer, Transform> {
+class Renamer final : public utils::Castable<Renamer, Transform> {
   public:
     /// Data is outputted by the Renamer transform.
     /// Data holds information about shader usage and constant buffer offsets.
-    struct Data final : public Castable<Data, transform::Data> {
+    struct Data final : public utils::Castable<Data, transform::Data> {
         /// Remappings is a map of old symbol name to new symbol name
         using Remappings = std::unordered_map<std::string, std::string>;
 
@@ -59,7 +59,7 @@
 
     /// Optional configuration options for the transform.
     /// If omitted, then the renamer will use Target::kAll.
-    struct Config final : public Castable<Config, transform::Data> {
+    struct Config final : public utils::Castable<Config, transform::Data> {
         /// Constructor
         /// @param tgt the targets to rename
         /// @param keep_unicode if false, symbols with non-ascii code-points are
diff --git a/src/tint/transform/robustness.h b/src/tint/transform/robustness.h
index 596db4d..e916b16 100644
--- a/src/tint/transform/robustness.h
+++ b/src/tint/transform/robustness.h
@@ -29,7 +29,7 @@
 ///       * BuiltinPolyfill as 'clamp' and binary operators may need to be polyfilled.
 ///       * CanonicalizeEntryPointIO as the transform does not support the 'in' and 'out' address
 ///         spaces.
-class Robustness final : public Castable<Robustness, Transform> {
+class Robustness final : public utils::Castable<Robustness, Transform> {
   public:
     /// Robustness action for out-of-bounds indexing.
     enum class Action {
@@ -45,7 +45,7 @@
     };
 
     /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
+    struct Config final : public utils::Castable<Config, Data> {
         /// Constructor
         Config();
 
diff --git a/src/tint/transform/simplify_pointers.h b/src/tint/transform/simplify_pointers.h
index 6e040bb..8783d43 100644
--- a/src/tint/transform/simplify_pointers.h
+++ b/src/tint/transform/simplify_pointers.h
@@ -31,7 +31,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * Unshadow
-class SimplifyPointers final : public Castable<SimplifyPointers, Transform> {
+class SimplifyPointers final : public utils::Castable<SimplifyPointers, Transform> {
   public:
     /// Constructor
     SimplifyPointers();
diff --git a/src/tint/transform/single_entry_point.h b/src/tint/transform/single_entry_point.h
index 7aba5e8..b91856b 100644
--- a/src/tint/transform/single_entry_point.h
+++ b/src/tint/transform/single_entry_point.h
@@ -25,10 +25,10 @@
 ///
 /// All module-scope variables, types, and functions that are not used by the
 /// target entry point will also be removed.
-class SingleEntryPoint final : public Castable<SingleEntryPoint, Transform> {
+class SingleEntryPoint final : public utils::Castable<SingleEntryPoint, Transform> {
   public:
     /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
+    struct Config final : public utils::Castable<Config, Data> {
         /// Constructor
         /// @param entry_point the name of the entry point to keep
         explicit Config(std::string entry_point = "");
diff --git a/src/tint/transform/spirv_atomic.h b/src/tint/transform/spirv_atomic.h
index b524200..907996b 100644
--- a/src/tint/transform/spirv_atomic.h
+++ b/src/tint/transform/spirv_atomic.h
@@ -32,7 +32,7 @@
 /// with calls to the WGSL atomic builtin. It also makes sure to replace variable declarations that
 /// are the target of the atomic operations with an atomic declaration of the same type. For
 /// structs, it creates a copy of the original struct with atomic members.
-class SpirvAtomic final : public Castable<SpirvAtomic, Transform> {
+class SpirvAtomic final : public utils::Castable<SpirvAtomic, Transform> {
   public:
     /// Constructor
     SpirvAtomic();
@@ -41,7 +41,7 @@
 
     /// Stub is an attribute applied to stub SPIR-V reader generated functions that need to be
     /// translated to an atomic builtin.
-    class Stub final : public Castable<Stub, ast::InternalAttribute> {
+    class Stub final : public utils::Castable<Stub, ast::InternalAttribute> {
       public:
         /// @param pid the identifier of the program that owns this node
         /// @param nid the unique node identifier
diff --git a/src/tint/transform/std140.h b/src/tint/transform/std140.h
index 769932f..ab1b31e 100644
--- a/src/tint/transform/std140.h
+++ b/src/tint/transform/std140.h
@@ -28,7 +28,7 @@
 /// sufficient to have any WGSL structure be std140-layout conformant.
 ///
 /// @note This transform requires the PromoteSideEffectsToDecl transform to have been run first.
-class Std140 final : public Castable<Std140, Transform> {
+class Std140 final : public utils::Castable<Std140, Transform> {
   public:
     /// Constructor
     Std140();
diff --git a/src/tint/transform/substitute_override.h b/src/tint/transform/substitute_override.h
index 853acc7..10200ca 100644
--- a/src/tint/transform/substitute_override.h
+++ b/src/tint/transform/substitute_override.h
@@ -43,10 +43,10 @@
 /// ```
 ///
 /// @see crbug.com/tint/1582
-class SubstituteOverride final : public Castable<SubstituteOverride, Transform> {
+class SubstituteOverride final : public utils::Castable<SubstituteOverride, Transform> {
   public:
     /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
+    struct Config final : public utils::Castable<Config, Data> {
         /// Constructor
         Config();
 
diff --git a/src/tint/transform/texture_1d_to_2d.h b/src/tint/transform/texture_1d_to_2d.h
index 9999821..c742e2a 100644
--- a/src/tint/transform/texture_1d_to_2d.h
+++ b/src/tint/transform/texture_1d_to_2d.h
@@ -21,7 +21,7 @@
 
 /// This transform converts all 1D texture types and accesses to 2D.
 /// This is required for GLSL ES, which does not support 1D textures.
-class Texture1DTo2D final : public Castable<Texture1DTo2D, Transform> {
+class Texture1DTo2D final : public utils::Castable<Texture1DTo2D, Transform> {
   public:
     /// Constructor
     Texture1DTo2D();
diff --git a/src/tint/transform/transform.h b/src/tint/transform/transform.h
index c9c1eee..b498e82 100644
--- a/src/tint/transform/transform.h
+++ b/src/tint/transform/transform.h
@@ -19,14 +19,14 @@
 #include <unordered_map>
 #include <utility>
 
-#include "src/tint/castable.h"
 #include "src/tint/program.h"
+#include "src/tint/utils/castable.h"
 
 namespace tint::transform {
 
 /// Data is the base class for transforms that accept extra input or emit extra
 /// output information along with a Program.
-class Data : public Castable<Data> {
+class Data : public utils::Castable<Data> {
   public:
     /// Constructor
     Data();
@@ -72,7 +72,7 @@
     template <typename T>
     void Put(std::unique_ptr<T>&& data) {
         static_assert(std::is_base_of<Data, T>::value, "T does not derive from Data");
-        map_[&TypeInfo::Of<T>()] = std::move(data);
+        map_[&utils::TypeInfo::Of<T>()] = std::move(data);
     }
 
     /// Creates the data of type `T` with the provided arguments and adds it into
@@ -94,7 +94,7 @@
     /// Put()
     template <typename T>
     T* Get() {
-        auto it = map_.find(&TypeInfo::Of<T>());
+        auto it = map_.find(&utils::TypeInfo::Of<T>());
         if (it == map_.end()) {
             return nullptr;
         }
@@ -122,7 +122,7 @@
         PutAll(std::forward<Tn>(remainder)...);
     }
 
-    std::unordered_map<const TypeInfo*, std::unique_ptr<Data>> map_;
+    std::unordered_map<const utils::TypeInfo*, std::unique_ptr<Data>> map_;
 };
 
 /// The return type of Run()
@@ -151,7 +151,7 @@
 };
 
 /// Interface for Program transforms
-class Transform : public Castable<Transform> {
+class Transform : public utils::Castable<Transform> {
   public:
     /// Constructor
     Transform();
diff --git a/src/tint/transform/truncate_interstage_variables.cc b/src/tint/transform/truncate_interstage_variables.cc
index b4ae24e..269b171 100644
--- a/src/tint/transform/truncate_interstage_variables.cc
+++ b/src/tint/transform/truncate_interstage_variables.cc
@@ -57,7 +57,7 @@
         b.Diagnostics().add_error(
             diag::System::Transform,
             "missing transform data for " +
-                std::string(TypeInfo::Of<TruncateInterstageVariables>().name));
+                std::string(utils::TypeInfo::Of<TruncateInterstageVariables>().name));
         return Program(std::move(b));
     }
 
diff --git a/src/tint/transform/truncate_interstage_variables.h b/src/tint/transform/truncate_interstage_variables.h
index bed226c..f279eb3 100644
--- a/src/tint/transform/truncate_interstage_variables.h
+++ b/src/tint/transform/truncate_interstage_variables.h
@@ -88,10 +88,11 @@
 ///  }
 /// ```
 ///
-class TruncateInterstageVariables final : public Castable<TruncateInterstageVariables, Transform> {
+class TruncateInterstageVariables final
+    : public utils::Castable<TruncateInterstageVariables, Transform> {
   public:
     /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
+    struct Config final : public utils::Castable<Config, Data> {
         /// Constructor
         Config();
 
diff --git a/src/tint/transform/unshadow.h b/src/tint/transform/unshadow.h
index 9030006..313e556 100644
--- a/src/tint/transform/unshadow.h
+++ b/src/tint/transform/unshadow.h
@@ -20,7 +20,7 @@
 namespace tint::transform {
 
 /// Unshadow is a Transform that renames any variables that shadow another variable.
-class Unshadow final : public Castable<Unshadow, Transform> {
+class Unshadow final : public utils::Castable<Unshadow, Transform> {
   public:
     /// Constructor
     Unshadow();
diff --git a/src/tint/transform/vectorize_matrix_conversions.h b/src/tint/transform/vectorize_matrix_conversions.h
index c86240c..2308eb1 100644
--- a/src/tint/transform/vectorize_matrix_conversions.h
+++ b/src/tint/transform/vectorize_matrix_conversions.h
@@ -20,7 +20,8 @@
 namespace tint::transform {
 
 /// A transform that converts matrix conversions (between f32 and f16 matrices) to the vector form.
-class VectorizeMatrixConversions final : public Castable<VectorizeMatrixConversions, Transform> {
+class VectorizeMatrixConversions final
+    : public utils::Castable<VectorizeMatrixConversions, Transform> {
   public:
     /// Constructor
     VectorizeMatrixConversions();
diff --git a/src/tint/transform/vectorize_scalar_matrix_initializers.h b/src/tint/transform/vectorize_scalar_matrix_initializers.h
index f9c0164..f0aba6e 100644
--- a/src/tint/transform/vectorize_scalar_matrix_initializers.h
+++ b/src/tint/transform/vectorize_scalar_matrix_initializers.h
@@ -21,7 +21,7 @@
 
 /// A transform that converts scalar matrix initializers to the vector form.
 class VectorizeScalarMatrixInitializers final
-    : public Castable<VectorizeScalarMatrixInitializers, Transform> {
+    : public utils::Castable<VectorizeScalarMatrixInitializers, Transform> {
   public:
     /// Constructor
     VectorizeScalarMatrixInitializers();
diff --git a/src/tint/transform/vertex_pulling.h b/src/tint/transform/vertex_pulling.h
index c0f88a5..d3849f7 100644
--- a/src/tint/transform/vertex_pulling.h
+++ b/src/tint/transform/vertex_pulling.h
@@ -137,10 +137,10 @@
 /// shader to use.
 ///
 /// The SingleEntryPoint transform must have run before VertexPulling.
-class VertexPulling final : public Castable<VertexPulling, Transform> {
+class VertexPulling final : public utils::Castable<VertexPulling, Transform> {
   public:
     /// Configuration options for the transform
-    struct Config final : public Castable<Config, Data> {
+    struct Config final : public utils::Castable<Config, Data> {
         /// Constructor
         Config();
 
diff --git a/src/tint/transform/while_to_loop.h b/src/tint/transform/while_to_loop.h
index 187799a..91e5b0c 100644
--- a/src/tint/transform/while_to_loop.h
+++ b/src/tint/transform/while_to_loop.h
@@ -21,7 +21,7 @@
 
 /// WhileToLoop is a Transform that converts a while statement into a loop
 /// statement. This is required by the SPIR-V writer.
-class WhileToLoop final : public Castable<WhileToLoop, Transform> {
+class WhileToLoop final : public utils::Castable<WhileToLoop, Transform> {
   public:
     /// Constructor
     WhileToLoop();
diff --git a/src/tint/transform/zero_init_workgroup_memory.h b/src/tint/transform/zero_init_workgroup_memory.h
index 64f4da8..24ba226 100644
--- a/src/tint/transform/zero_init_workgroup_memory.h
+++ b/src/tint/transform/zero_init_workgroup_memory.h
@@ -22,7 +22,7 @@
 /// ZeroInitWorkgroupMemory is a transform that injects code at the top of entry
 /// points to zero-initialize workgroup memory used by that entry point (and all
 /// transitive functions called by that entry point)
-class ZeroInitWorkgroupMemory final : public Castable<ZeroInitWorkgroupMemory, Transform> {
+class ZeroInitWorkgroupMemory final : public utils::Castable<ZeroInitWorkgroupMemory, Transform> {
   public:
     /// Constructor
     ZeroInitWorkgroupMemory();
diff --git a/src/tint/type/abstract_float.cc b/src/tint/type/abstract_float.cc
index 74e5d34..41e0430 100644
--- a/src/tint/type/abstract_float.cc
+++ b/src/tint/type/abstract_float.cc
@@ -21,7 +21,9 @@
 
 namespace tint::type {
 
-AbstractFloat::AbstractFloat() : Base(utils::Hash(TypeInfo::Of<AbstractFloat>().full_hashcode)) {}
+AbstractFloat::AbstractFloat()
+    : Base(utils::Hash(utils::TypeInfo::Of<AbstractFloat>().full_hashcode)) {}
+
 AbstractFloat::~AbstractFloat() = default;
 
 bool AbstractFloat::Equals(const UniqueNode& other) const {
diff --git a/src/tint/type/abstract_float.h b/src/tint/type/abstract_float.h
index 2c6ac09..10d6f3a 100644
--- a/src/tint/type/abstract_float.h
+++ b/src/tint/type/abstract_float.h
@@ -23,7 +23,7 @@
 
 /// An abstract-float type.
 /// @see https://www.w3.org/TR/WGSL/#abstractFloat
-class AbstractFloat final : public Castable<AbstractFloat, AbstractNumeric> {
+class AbstractFloat final : public utils::Castable<AbstractFloat, AbstractNumeric> {
   public:
     /// Constructor
     AbstractFloat();
diff --git a/src/tint/type/abstract_int.cc b/src/tint/type/abstract_int.cc
index 7fc883d..6b97622 100644
--- a/src/tint/type/abstract_int.cc
+++ b/src/tint/type/abstract_int.cc
@@ -21,7 +21,7 @@
 
 namespace tint::type {
 
-AbstractInt::AbstractInt() : Base(utils::Hash(TypeInfo::Of<AbstractInt>().full_hashcode)) {}
+AbstractInt::AbstractInt() : Base(utils::Hash(utils::TypeInfo::Of<AbstractInt>().full_hashcode)) {}
 
 AbstractInt::~AbstractInt() = default;
 
diff --git a/src/tint/type/abstract_int.h b/src/tint/type/abstract_int.h
index f1f67d8..aaaaadd 100644
--- a/src/tint/type/abstract_int.h
+++ b/src/tint/type/abstract_int.h
@@ -23,7 +23,7 @@
 
 /// An abstract-int type.
 /// @see https://www.w3.org/TR/WGSL/#abstractint
-class AbstractInt final : public Castable<AbstractInt, AbstractNumeric> {
+class AbstractInt final : public utils::Castable<AbstractInt, AbstractNumeric> {
   public:
     /// Constructor
     AbstractInt();
diff --git a/src/tint/type/abstract_numeric.h b/src/tint/type/abstract_numeric.h
index 5b6b9f4..217b2a2 100644
--- a/src/tint/type/abstract_numeric.h
+++ b/src/tint/type/abstract_numeric.h
@@ -23,7 +23,7 @@
 
 /// The base class for abstract-int and abstract-float types.
 /// @see https://www.w3.org/TR/WGSL/#types-for-creation-time-constants
-class AbstractNumeric : public Castable<AbstractNumeric, Type> {
+class AbstractNumeric : public utils::Castable<AbstractNumeric, Type> {
   public:
     /// Constructor
     /// @param hash the unique hash of the node
diff --git a/src/tint/type/array.cc b/src/tint/type/array.cc
index 71ec6b3..31893f8 100644
--- a/src/tint/type/array.cc
+++ b/src/tint/type/array.cc
@@ -60,7 +60,7 @@
              uint32_t size,
              uint32_t stride,
              uint32_t implicit_stride)
-    : Base(utils::Hash(TypeInfo::Of<Array>().full_hashcode, count, align, size, stride),
+    : Base(utils::Hash(utils::TypeInfo::Of<Array>().full_hashcode, count, align, size, stride),
            FlagsFrom(element, count)),
       element_(element),
       count_(count),
diff --git a/src/tint/type/array.h b/src/tint/type/array.h
index 79b8e3c..c400475 100644
--- a/src/tint/type/array.h
+++ b/src/tint/type/array.h
@@ -28,7 +28,7 @@
 namespace tint::type {
 
 /// Array holds the type information for Array nodes.
-class Array final : public Castable<Array, Type> {
+class Array final : public utils::Castable<Array, Type> {
   public:
     /// An error message string stating that the array count was expected to be a constant
     /// expression. Used by multiple writers and transforms.
diff --git a/src/tint/type/array_count.cc b/src/tint/type/array_count.cc
index 8ddda4c..7b47300 100644
--- a/src/tint/type/array_count.cc
+++ b/src/tint/type/array_count.cc
@@ -26,7 +26,8 @@
 ArrayCount::~ArrayCount() = default;
 
 ConstantArrayCount::ConstantArrayCount(uint32_t val)
-    : Base(static_cast<size_t>(TypeInfo::Of<ConstantArrayCount>().full_hashcode)), value(val) {}
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<ConstantArrayCount>().full_hashcode)),
+      value(val) {}
 ConstantArrayCount::~ConstantArrayCount() = default;
 
 bool ConstantArrayCount::Equals(const UniqueNode& other) const {
@@ -45,7 +46,7 @@
 }
 
 RuntimeArrayCount::RuntimeArrayCount()
-    : Base(static_cast<size_t>(TypeInfo::Of<RuntimeArrayCount>().full_hashcode)) {}
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<RuntimeArrayCount>().full_hashcode)) {}
 RuntimeArrayCount::~RuntimeArrayCount() = default;
 
 bool RuntimeArrayCount::Equals(const UniqueNode& other) const {
diff --git a/src/tint/type/array_count.h b/src/tint/type/array_count.h
index 94dc135..172d2d3 100644
--- a/src/tint/type/array_count.h
+++ b/src/tint/type/array_count.h
@@ -25,7 +25,7 @@
 namespace tint::type {
 
 /// An array count
-class ArrayCount : public Castable<ArrayCount, UniqueNode> {
+class ArrayCount : public utils::Castable<ArrayCount, UniqueNode> {
   public:
     ~ArrayCount() override;
 
@@ -48,7 +48,7 @@
 /// const N = 123;
 /// type arr = array<i32, N>
 /// ```
-class ConstantArrayCount final : public Castable<ConstantArrayCount, ArrayCount> {
+class ConstantArrayCount final : public utils::Castable<ConstantArrayCount, ArrayCount> {
   public:
     /// Constructor
     /// @param val the constant-expression value
@@ -75,7 +75,7 @@
 /// ```
 /// type arr = array<i32>
 /// ```
-class RuntimeArrayCount final : public Castable<RuntimeArrayCount, ArrayCount> {
+class RuntimeArrayCount final : public utils::Castable<RuntimeArrayCount, ArrayCount> {
   public:
     /// Constructor
     RuntimeArrayCount();
diff --git a/src/tint/type/atomic.cc b/src/tint/type/atomic.cc
index a1d5da1..0189301 100644
--- a/src/tint/type/atomic.cc
+++ b/src/tint/type/atomic.cc
@@ -26,7 +26,7 @@
 namespace tint::type {
 
 Atomic::Atomic(const type::Type* subtype)
-    : Base(utils::Hash(TypeInfo::Of<Atomic>().full_hashcode, subtype),
+    : Base(utils::Hash(utils::TypeInfo::Of<Atomic>().full_hashcode, subtype),
            type::Flags{
                Flag::kCreationFixedFootprint,
                Flag::kFixedFootprint,
diff --git a/src/tint/type/atomic.h b/src/tint/type/atomic.h
index 4f304f0..a19239b 100644
--- a/src/tint/type/atomic.h
+++ b/src/tint/type/atomic.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A atomic type.
-class Atomic final : public Castable<Atomic, Type> {
+class Atomic final : public utils::Castable<Atomic, Type> {
   public:
     /// Constructor
     /// @param subtype the atomic type
diff --git a/src/tint/type/bool.cc b/src/tint/type/bool.cc
index 6c972c3..26741c5 100644
--- a/src/tint/type/bool.cc
+++ b/src/tint/type/bool.cc
@@ -21,7 +21,7 @@
 namespace tint::type {
 
 Bool::Bool()
-    : Base(static_cast<size_t>(TypeInfo::Of<Bool>().full_hashcode),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<Bool>().full_hashcode),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/bool.h b/src/tint/type/bool.h
index 6173bbb..33906b8 100644
--- a/src/tint/type/bool.h
+++ b/src/tint/type/bool.h
@@ -28,7 +28,7 @@
 namespace tint::type {
 
 /// A boolean type
-class Bool final : public Castable<Bool, Type> {
+class Bool final : public utils::Castable<Bool, Type> {
   public:
     /// Constructor
     Bool();
diff --git a/src/tint/type/depth_multisampled_texture.cc b/src/tint/type/depth_multisampled_texture.cc
index 765fec3..7109920 100644
--- a/src/tint/type/depth_multisampled_texture.cc
+++ b/src/tint/type/depth_multisampled_texture.cc
@@ -33,7 +33,7 @@
 }  // namespace
 
 DepthMultisampledTexture::DepthMultisampledTexture(TextureDimension dim)
-    : Base(utils::Hash(TypeInfo::Of<DepthMultisampledTexture>().full_hashcode, dim), dim) {
+    : Base(utils::Hash(utils::TypeInfo::Of<DepthMultisampledTexture>().full_hashcode, dim), dim) {
     TINT_ASSERT(Type, IsValidDepthDimension(dim));
 }
 
diff --git a/src/tint/type/depth_multisampled_texture.h b/src/tint/type/depth_multisampled_texture.h
index 4a6d3c2..9a72d61 100644
--- a/src/tint/type/depth_multisampled_texture.h
+++ b/src/tint/type/depth_multisampled_texture.h
@@ -23,7 +23,7 @@
 namespace tint::type {
 
 /// A multisampled depth texture type.
-class DepthMultisampledTexture final : public Castable<DepthMultisampledTexture, Texture> {
+class DepthMultisampledTexture final : public utils::Castable<DepthMultisampledTexture, Texture> {
   public:
     /// Constructor
     /// @param dim the dimensionality of the texture
diff --git a/src/tint/type/depth_texture.cc b/src/tint/type/depth_texture.cc
index 1d6eab5..d4476fb 100644
--- a/src/tint/type/depth_texture.cc
+++ b/src/tint/type/depth_texture.cc
@@ -34,7 +34,7 @@
 }  // namespace
 
 DepthTexture::DepthTexture(TextureDimension dim)
-    : Base(utils::Hash(TypeInfo::Of<DepthTexture>().full_hashcode, dim), dim) {
+    : Base(utils::Hash(utils::TypeInfo::Of<DepthTexture>().full_hashcode, dim), dim) {
     TINT_ASSERT(Type, IsValidDepthDimension(dim));
 }
 
diff --git a/src/tint/type/depth_texture.h b/src/tint/type/depth_texture.h
index 079b5fb..8e8c47d 100644
--- a/src/tint/type/depth_texture.h
+++ b/src/tint/type/depth_texture.h
@@ -23,7 +23,7 @@
 namespace tint::type {
 
 /// A depth texture type.
-class DepthTexture final : public Castable<DepthTexture, Texture> {
+class DepthTexture final : public utils::Castable<DepthTexture, Texture> {
   public:
     /// Constructor
     /// @param dim the dimensionality of the texture
diff --git a/src/tint/type/external_texture.cc b/src/tint/type/external_texture.cc
index 85469a7..924d06a 100644
--- a/src/tint/type/external_texture.cc
+++ b/src/tint/type/external_texture.cc
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 ExternalTexture::ExternalTexture()
-    : Base(static_cast<size_t>(TypeInfo::Of<ExternalTexture>().full_hashcode),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<ExternalTexture>().full_hashcode),
            TextureDimension::k2d) {}
 
 ExternalTexture::~ExternalTexture() = default;
diff --git a/src/tint/type/external_texture.h b/src/tint/type/external_texture.h
index be46048..219e7b8 100644
--- a/src/tint/type/external_texture.h
+++ b/src/tint/type/external_texture.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// An external texture type
-class ExternalTexture final : public Castable<ExternalTexture, Texture> {
+class ExternalTexture final : public utils::Castable<ExternalTexture, Texture> {
   public:
     /// Constructor
     ExternalTexture();
diff --git a/src/tint/type/f16.cc b/src/tint/type/f16.cc
index 842d147..288f14a 100644
--- a/src/tint/type/f16.cc
+++ b/src/tint/type/f16.cc
@@ -21,7 +21,7 @@
 namespace tint::type {
 
 F16::F16()
-    : Base(static_cast<size_t>(TypeInfo::Of<F16>().full_hashcode),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<F16>().full_hashcode),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/f16.h b/src/tint/type/f16.h
index 21100ff..c7fc3eb 100644
--- a/src/tint/type/f16.h
+++ b/src/tint/type/f16.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A float 16 type
-class F16 final : public Castable<F16, Type> {
+class F16 final : public utils::Castable<F16, Type> {
   public:
     /// Constructor
     F16();
diff --git a/src/tint/type/f32.cc b/src/tint/type/f32.cc
index b7c5135..e3afcc7 100644
--- a/src/tint/type/f32.cc
+++ b/src/tint/type/f32.cc
@@ -21,7 +21,7 @@
 namespace tint::type {
 
 F32::F32()
-    : Base(static_cast<size_t>(TypeInfo::Of<F32>().full_hashcode),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<F32>().full_hashcode),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/f32.h b/src/tint/type/f32.h
index 397911d..68b3c77 100644
--- a/src/tint/type/f32.h
+++ b/src/tint/type/f32.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A float 32 type
-class F32 final : public Castable<F32, Type> {
+class F32 final : public utils::Castable<F32, Type> {
   public:
     /// Constructor
     F32();
diff --git a/src/tint/type/i32.cc b/src/tint/type/i32.cc
index 045706d..66da527 100644
--- a/src/tint/type/i32.cc
+++ b/src/tint/type/i32.cc
@@ -21,7 +21,7 @@
 namespace tint::type {
 
 I32::I32()
-    : Base(static_cast<size_t>(TypeInfo::Of<I32>().full_hashcode),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<I32>().full_hashcode),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/i32.h b/src/tint/type/i32.h
index ddf59b4..563ee69 100644
--- a/src/tint/type/i32.h
+++ b/src/tint/type/i32.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A signed int 32 type.
-class I32 final : public Castable<I32, Type> {
+class I32 final : public utils::Castable<I32, Type> {
   public:
     /// Constructor
     I32();
diff --git a/src/tint/type/matrix.cc b/src/tint/type/matrix.cc
index 59dfaa9..35664f3 100644
--- a/src/tint/type/matrix.cc
+++ b/src/tint/type/matrix.cc
@@ -26,7 +26,7 @@
 namespace tint::type {
 
 Matrix::Matrix(const Vector* column_type, uint32_t columns)
-    : Base(utils::Hash(TypeInfo::Of<Vector>().full_hashcode, columns, column_type),
+    : Base(utils::Hash(utils::TypeInfo::Of<Vector>().full_hashcode, columns, column_type),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/matrix.h b/src/tint/type/matrix.h
index c6707a2..1519d6a 100644
--- a/src/tint/type/matrix.h
+++ b/src/tint/type/matrix.h
@@ -27,7 +27,7 @@
 namespace tint::type {
 
 /// A matrix type
-class Matrix final : public Castable<Matrix, Type> {
+class Matrix final : public utils::Castable<Matrix, Type> {
   public:
     /// Constructor
     /// @param column_type the type of a column of the matrix
diff --git a/src/tint/type/multisampled_texture.cc b/src/tint/type/multisampled_texture.cc
index b63c681..850b11b 100644
--- a/src/tint/type/multisampled_texture.cc
+++ b/src/tint/type/multisampled_texture.cc
@@ -26,7 +26,7 @@
 namespace tint::type {
 
 MultisampledTexture::MultisampledTexture(TextureDimension dim, const Type* type)
-    : Base(utils::Hash(TypeInfo::Of<MultisampledTexture>().full_hashcode, dim, type), dim),
+    : Base(utils::Hash(utils::TypeInfo::Of<MultisampledTexture>().full_hashcode, dim, type), dim),
       type_(type) {
     TINT_ASSERT(Type, type_);
 }
diff --git a/src/tint/type/multisampled_texture.h b/src/tint/type/multisampled_texture.h
index d6c2e50..89575ee 100644
--- a/src/tint/type/multisampled_texture.h
+++ b/src/tint/type/multisampled_texture.h
@@ -23,7 +23,7 @@
 namespace tint::type {
 
 /// A multisampled texture type.
-class MultisampledTexture final : public Castable<MultisampledTexture, Texture> {
+class MultisampledTexture final : public utils::Castable<MultisampledTexture, Texture> {
   public:
     /// Constructor
     /// @param dim the dimensionality of the texture
diff --git a/src/tint/type/node.h b/src/tint/type/node.h
index eddcb27..237c3ec 100644
--- a/src/tint/type/node.h
+++ b/src/tint/type/node.h
@@ -15,12 +15,12 @@
 #ifndef SRC_TINT_TYPE_NODE_H_
 #define SRC_TINT_TYPE_NODE_H_
 
-#include "src/tint/castable.h"
+#include "src/tint/utils/castable.h"
 
 namespace tint::type {
 
 /// Node is the base class for all type nodes
-class Node : public Castable<Node> {
+class Node : public utils::Castable<Node> {
   public:
     /// Constructor
     Node();
diff --git a/src/tint/type/pointer.cc b/src/tint/type/pointer.cc
index 2922b0e..636f8e5 100644
--- a/src/tint/type/pointer.cc
+++ b/src/tint/type/pointer.cc
@@ -26,8 +26,9 @@
 namespace tint::type {
 
 Pointer::Pointer(const Type* subtype, builtin::AddressSpace address_space, builtin::Access access)
-    : Base(utils::Hash(TypeInfo::Of<Pointer>().full_hashcode, address_space, subtype, access),
-           type::Flags{}),
+    : Base(
+          utils::Hash(utils::TypeInfo::Of<Pointer>().full_hashcode, address_space, subtype, access),
+          type::Flags{}),
       subtype_(subtype),
       address_space_(address_space),
       access_(access) {
diff --git a/src/tint/type/pointer.h b/src/tint/type/pointer.h
index 0eed27c..80626e9 100644
--- a/src/tint/type/pointer.h
+++ b/src/tint/type/pointer.h
@@ -24,7 +24,7 @@
 namespace tint::type {
 
 /// A pointer type.
-class Pointer final : public Castable<Pointer, Type> {
+class Pointer final : public utils::Castable<Pointer, Type> {
   public:
     /// Constructor
     /// @param subtype the pointee type
diff --git a/src/tint/type/reference.cc b/src/tint/type/reference.cc
index bf2d22c..e0fd92a 100644
--- a/src/tint/type/reference.cc
+++ b/src/tint/type/reference.cc
@@ -27,7 +27,10 @@
 Reference::Reference(const Type* subtype,
                      builtin::AddressSpace address_space,
                      builtin::Access access)
-    : Base(utils::Hash(TypeInfo::Of<Reference>().full_hashcode, address_space, subtype, access),
+    : Base(utils::Hash(utils::TypeInfo::Of<Reference>().full_hashcode,
+                       address_space,
+                       subtype,
+                       access),
            type::Flags{}),
       subtype_(subtype),
       address_space_(address_space),
diff --git a/src/tint/type/reference.h b/src/tint/type/reference.h
index 794b48b..617b2ca 100644
--- a/src/tint/type/reference.h
+++ b/src/tint/type/reference.h
@@ -24,7 +24,7 @@
 namespace tint::type {
 
 /// A reference type.
-class Reference final : public Castable<Reference, Type> {
+class Reference final : public utils::Castable<Reference, Type> {
   public:
     /// Constructor
     /// @param subtype the pointee type
diff --git a/src/tint/type/sampled_texture.cc b/src/tint/type/sampled_texture.cc
index 8a4e440..2fb29f6 100644
--- a/src/tint/type/sampled_texture.cc
+++ b/src/tint/type/sampled_texture.cc
@@ -26,7 +26,8 @@
 namespace tint::type {
 
 SampledTexture::SampledTexture(TextureDimension dim, const Type* type)
-    : Base(utils::Hash(TypeInfo::Of<SampledTexture>().full_hashcode, dim, type), dim), type_(type) {
+    : Base(utils::Hash(utils::TypeInfo::Of<SampledTexture>().full_hashcode, dim, type), dim),
+      type_(type) {
     TINT_ASSERT(Type, type_);
 }
 
diff --git a/src/tint/type/sampled_texture.h b/src/tint/type/sampled_texture.h
index 35208b7..346741f 100644
--- a/src/tint/type/sampled_texture.h
+++ b/src/tint/type/sampled_texture.h
@@ -23,7 +23,7 @@
 namespace tint::type {
 
 /// A sampled texture type.
-class SampledTexture final : public Castable<SampledTexture, Texture> {
+class SampledTexture final : public utils::Castable<SampledTexture, Texture> {
   public:
     /// Constructor
     /// @param dim the dimensionality of the texture
diff --git a/src/tint/type/sampler.cc b/src/tint/type/sampler.cc
index 271b8be..2ad8f0c 100644
--- a/src/tint/type/sampler.cc
+++ b/src/tint/type/sampler.cc
@@ -22,7 +22,8 @@
 namespace tint::type {
 
 Sampler::Sampler(SamplerKind kind)
-    : Base(utils::Hash(TypeInfo::Of<Sampler>().full_hashcode, kind), type::Flags{}), kind_(kind) {}
+    : Base(utils::Hash(utils::TypeInfo::Of<Sampler>().full_hashcode, kind), type::Flags{}),
+      kind_(kind) {}
 
 Sampler::~Sampler() = default;
 
diff --git a/src/tint/type/sampler.h b/src/tint/type/sampler.h
index 9a3554f..d9f7286 100644
--- a/src/tint/type/sampler.h
+++ b/src/tint/type/sampler.h
@@ -23,7 +23,7 @@
 namespace tint::type {
 
 /// A sampler type.
-class Sampler final : public Castable<Sampler, Type> {
+class Sampler final : public utils::Castable<Sampler, Type> {
   public:
     /// Constructor
     /// @param kind the kind of sampler
diff --git a/src/tint/type/storage_texture.cc b/src/tint/type/storage_texture.cc
index 7aa84c1..cd41452 100644
--- a/src/tint/type/storage_texture.cc
+++ b/src/tint/type/storage_texture.cc
@@ -29,7 +29,8 @@
                                builtin::TexelFormat format,
                                builtin::Access access,
                                Type* subtype)
-    : Base(utils::Hash(TypeInfo::Of<StorageTexture>().full_hashcode, dim, format, access), dim),
+    : Base(utils::Hash(utils::TypeInfo::Of<StorageTexture>().full_hashcode, dim, format, access),
+           dim),
       texel_format_(format),
       access_(access),
       subtype_(subtype) {}
diff --git a/src/tint/type/storage_texture.h b/src/tint/type/storage_texture.h
index 206e403..abd580c 100644
--- a/src/tint/type/storage_texture.h
+++ b/src/tint/type/storage_texture.h
@@ -30,7 +30,7 @@
 namespace tint::type {
 
 /// A storage texture type.
-class StorageTexture final : public Castable<StorageTexture, Texture> {
+class StorageTexture final : public utils::Castable<StorageTexture, Texture> {
   public:
     /// Constructor
     /// @param dim the dimensionality of the texture
diff --git a/src/tint/type/struct.cc b/src/tint/type/struct.cc
index bffaa21..e5c8d4c 100644
--- a/src/tint/type/struct.cc
+++ b/src/tint/type/struct.cc
@@ -58,7 +58,7 @@
                uint32_t align,
                uint32_t size,
                uint32_t size_no_padding)
-    : Base(utils::Hash(TypeInfo::Of<Struct>().full_hashcode, name), FlagsFrom(members)),
+    : Base(utils::Hash(utils::TypeInfo::Of<Struct>().full_hashcode, name), FlagsFrom(members)),
       source_(source),
       name_(name),
       members_(std::move(members)),
diff --git a/src/tint/type/struct.h b/src/tint/type/struct.h
index 035ea7c..ea459f5 100644
--- a/src/tint/type/struct.h
+++ b/src/tint/type/struct.h
@@ -45,7 +45,7 @@
 };
 
 /// Struct holds the Type information for structures.
-class Struct : public Castable<Struct, Type> {
+class Struct : public utils::Castable<Struct, Type> {
   public:
     /// Constructor
     /// @param source the source of the structure
@@ -164,7 +164,7 @@
 };
 
 /// StructMember holds the type information for structure members.
-class StructMember : public Castable<StructMember, Node> {
+class StructMember : public utils::Castable<StructMember, Node> {
   public:
     /// Constructor
     /// @param source the source of the struct member
diff --git a/src/tint/type/texture.h b/src/tint/type/texture.h
index e7cbe81..db99ae5 100644
--- a/src/tint/type/texture.h
+++ b/src/tint/type/texture.h
@@ -21,7 +21,7 @@
 namespace tint::type {
 
 /// A texture type.
-class Texture : public Castable<Texture, Type> {
+class Texture : public utils::Castable<Texture, Type> {
   public:
     /// Constructor
     /// @param hash the unique hash of the node
diff --git a/src/tint/type/type.h b/src/tint/type/type.h
index 083934e..b6f34a6 100644
--- a/src/tint/type/type.h
+++ b/src/tint/type/type.h
@@ -47,7 +47,7 @@
 using Flags = utils::EnumSet<Flag>;
 
 /// Base class for a type in the system
-class Type : public Castable<Type, UniqueNode> {
+class Type : public utils::Castable<Type, UniqueNode> {
   public:
     /// Destructor
     ~Type() override;
diff --git a/src/tint/type/u32.cc b/src/tint/type/u32.cc
index f1eaf66..4e40c73 100644
--- a/src/tint/type/u32.cc
+++ b/src/tint/type/u32.cc
@@ -21,7 +21,7 @@
 namespace tint::type {
 
 U32::U32()
-    : Base(static_cast<size_t>(TypeInfo::Of<U32>().full_hashcode),
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<U32>().full_hashcode),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/u32.h b/src/tint/type/u32.h
index 456436a..de9b550 100644
--- a/src/tint/type/u32.h
+++ b/src/tint/type/u32.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A unsigned int 32 type.
-class U32 final : public Castable<U32, Type> {
+class U32 final : public utils::Castable<U32, Type> {
   public:
     /// Constructor
     U32();
diff --git a/src/tint/type/unique_node.h b/src/tint/type/unique_node.h
index 1fd6011..0e642df 100644
--- a/src/tint/type/unique_node.h
+++ b/src/tint/type/unique_node.h
@@ -25,7 +25,7 @@
 /// Deduplication is achieved by comparing a temporary object to the set of existing objects, using
 /// Hash() and Equals(). If an existing object is found, then the pointer to that object is
 /// returned, otherwise a new object is constructed, added to the Manager's set and returned.
-class UniqueNode : public Castable<UniqueNode, Node> {
+class UniqueNode : public utils::Castable<UniqueNode, Node> {
   public:
     /// Constructor
     /// @param hash the immutable hash for the node
diff --git a/src/tint/type/vector.cc b/src/tint/type/vector.cc
index 3ee68e9..9c5bebd 100644
--- a/src/tint/type/vector.cc
+++ b/src/tint/type/vector.cc
@@ -25,7 +25,7 @@
 namespace tint::type {
 
 Vector::Vector(Type const* subtype, uint32_t width, bool packed /* = false */)
-    : Base(utils::Hash(TypeInfo::Of<Vector>().full_hashcode, width, subtype, packed),
+    : Base(utils::Hash(utils::TypeInfo::Of<Vector>().full_hashcode, width, subtype, packed),
            type::Flags{
                Flag::kConstructable,
                Flag::kCreationFixedFootprint,
diff --git a/src/tint/type/vector.h b/src/tint/type/vector.h
index 04e349a..47b5c40 100644
--- a/src/tint/type/vector.h
+++ b/src/tint/type/vector.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A vector type.
-class Vector : public Castable<Vector, Type> {
+class Vector : public utils::Castable<Vector, Type> {
   public:
     /// Constructor
     /// @param subtype the vector element type
diff --git a/src/tint/type/void.cc b/src/tint/type/void.cc
index c562a52..2b8ddf7 100644
--- a/src/tint/type/void.cc
+++ b/src/tint/type/void.cc
@@ -20,7 +20,8 @@
 
 namespace tint::type {
 
-Void::Void() : Base(static_cast<size_t>(TypeInfo::Of<Void>().full_hashcode), type::Flags{}) {}
+Void::Void()
+    : Base(static_cast<size_t>(utils::TypeInfo::Of<Void>().full_hashcode), type::Flags{}) {}
 
 Void::~Void() = default;
 
diff --git a/src/tint/type/void.h b/src/tint/type/void.h
index b24ea9f..0215c08 100644
--- a/src/tint/type/void.h
+++ b/src/tint/type/void.h
@@ -22,7 +22,7 @@
 namespace tint::type {
 
 /// A void type
-class Void final : public Castable<Void, Type> {
+class Void final : public utils::Castable<Void, Type> {
   public:
     /// Constructor
     Void();
diff --git a/src/tint/castable.cc b/src/tint/utils/castable.cc
similarity index 81%
rename from src/tint/castable.cc
rename to src/tint/utils/castable.cc
index 40c32da..b7756b3 100644
--- a/src/tint/castable.cc
+++ b/src/tint/utils/castable.cc
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/castable.h"
+#include "src/tint/utils/castable.h"
 
-namespace tint {
+namespace tint::utils {
 
 /// The unique TypeInfo for the CastableBase type
 /// @return doxygen-thinks-this-static-field-is-a-function :(
@@ -22,12 +22,12 @@
 const TypeInfo detail::TypeInfoOf<CastableBase>::info{
     nullptr,
     "CastableBase",
-    tint::TypeInfo::HashCodeOf<CastableBase>(),
-    tint::TypeInfo::FullHashCodeOf<CastableBase>(),
+    tint::utils::TypeInfo::HashCodeOf<CastableBase>(),
+    tint::utils::TypeInfo::FullHashCodeOf<CastableBase>(),
 };
 
 CastableBase::CastableBase(const CastableBase&) = default;
 
 CastableBase::~CastableBase() = default;
 
-}  // namespace tint
+}  // namespace tint::utils
diff --git a/src/tint/castable.h b/src/tint/utils/castable.h
similarity index 91%
rename from src/tint/castable.h
rename to src/tint/utils/castable.h
index 33d61396..abe7610 100644
--- a/src/tint/castable.h
+++ b/src/tint/utils/castable.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SRC_TINT_CASTABLE_H_
-#define SRC_TINT_CASTABLE_H_
+#ifndef SRC_TINT_UTILS_CASTABLE_H_
+#define SRC_TINT_UTILS_CASTABLE_H_
 
 #include <stdint.h>
 #include <functional>
@@ -42,20 +42,20 @@
 TINT_CASTABLE_PUSH_DISABLE_WARNINGS();
 
 // Forward declarations
-namespace tint {
+namespace tint::utils {
 class CastableBase;
 
 /// Ignore is used as a special type used for skipping over types for trait
 /// helper functions.
 class Ignore {};
-}  // namespace tint
+}  // namespace tint::utils
 
-namespace tint::detail {
+namespace tint::utils::detail {
 template <typename T>
 struct TypeInfoOf;
-}  // namespace tint::detail
+}  // namespace tint::utils::detail
 
-namespace tint {
+namespace tint::utils {
 
 /// True if all template types that are not Ignore derive from CastableBase
 template <typename... TYPES>
@@ -64,15 +64,15 @@
     !(std::is_same_v<TYPES, Ignore> && ...);
 
 /// Helper macro to instantiate the TypeInfo<T> template for `CLASS`.
-#define TINT_INSTANTIATE_TYPEINFO(CLASS)                        \
-    TINT_CASTABLE_PUSH_DISABLE_WARNINGS();                      \
-    template <>                                                 \
-    const tint::TypeInfo tint::detail::TypeInfoOf<CLASS>::info{ \
-        &tint::detail::TypeInfoOf<CLASS::TrueBase>::info,       \
-        #CLASS,                                                 \
-        tint::TypeInfo::HashCodeOf<CLASS>(),                    \
-        tint::TypeInfo::FullHashCodeOf<CLASS>(),                \
-    };                                                          \
+#define TINT_INSTANTIATE_TYPEINFO(CLASS)                                      \
+    TINT_CASTABLE_PUSH_DISABLE_WARNINGS();                                    \
+    template <>                                                               \
+    const tint::utils::TypeInfo tint::utils::detail::TypeInfoOf<CLASS>::info{ \
+        &tint::utils::detail::TypeInfoOf<CLASS::TrueBase>::info,              \
+        #CLASS,                                                               \
+        tint::utils::TypeInfo::HashCodeOf<CLASS>(),                           \
+        tint::utils::TypeInfo::FullHashCodeOf<CLASS>(),                       \
+    };                                                                        \
     TINT_CASTABLE_POP_DISABLE_WARNINGS()
 
 /// Bit flags that can be passed to the template parameter `FLAGS` of Is() and As().
@@ -127,7 +127,7 @@
     /// @param object the object type to test from, which must be, or derive from type `FROM`.
     /// @see CastFlags
     template <typename TO, typename FROM, int FLAGS = 0>
-    static inline bool Is(const tint::TypeInfo* object) {
+    static inline bool Is(const tint::utils::TypeInfo* object) {
         constexpr const bool downcast = std::is_base_of<FROM, TO>::value;
         constexpr const bool upcast = std::is_base_of<TO, FROM>::value;
         constexpr const bool nocast = std::is_same<FROM, TO>::value;
@@ -155,7 +155,7 @@
     /// @param type the test type info
     /// @returns true if the class with this TypeInfo is of, or derives from the
     /// class with the given TypeInfo.
-    inline bool Is(const tint::TypeInfo* type) const {
+    inline bool Is(const tint::utils::TypeInfo* type) const {
         if (!Maybe(type->hashcode, full_hashcode)) {
             return false;
         }
@@ -346,12 +346,12 @@
     CastableBase& operator=(const CastableBase& other) = default;
 
     /// @returns the TypeInfo of the object
-    inline const tint::TypeInfo& TypeInfo() const { return *type_info_; }
+    inline const tint::utils::TypeInfo& TypeInfo() const { return *type_info_; }
 
     /// @returns true if this object is of, or derives from the class `TO`
     template <typename TO>
     inline bool Is() const {
-        return tint::Is<TO>(this);
+        return tint::utils::Is<TO>(this);
     }
 
     /// @returns true if this object is of, or derives from the class `TO` and pred(const TO*)
@@ -360,13 +360,13 @@
     /// derives from the class `TO`.
     template <typename TO, int FLAGS = 0, typename Pred = detail::Infer>
     inline bool Is(Pred&& pred) const {
-        return tint::Is<TO, FLAGS>(this, std::forward<Pred>(pred));
+        return tint::utils::Is<TO, FLAGS>(this, std::forward<Pred>(pred));
     }
 
     /// @returns true if this object is of, or derives from any of the `TO` classes.
     template <typename... TO>
     inline bool IsAnyOf() const {
-        return tint::IsAnyOf<TO...>(this);
+        return tint::utils::IsAnyOf<TO...>(this);
     }
 
     /// @returns this object dynamically cast to the type `TO` or `nullptr` if this object does not
@@ -374,7 +374,7 @@
     /// @see CastFlags
     template <typename TO, int FLAGS = 0>
     inline TO* As() {
-        return tint::As<TO, FLAGS>(this);
+        return tint::utils::As<TO, FLAGS>(this);
     }
 
     /// @returns this object dynamically cast to the type `TO` or `nullptr` if this object does not
@@ -382,14 +382,14 @@
     /// @see CastFlags
     template <typename TO, int FLAGS = 0>
     inline const TO* As() const {
-        return tint::As<const TO, FLAGS>(this);
+        return tint::utils::As<const TO, FLAGS>(this);
     }
 
   protected:
     CastableBase() = default;
 
     /// The type information for the object
-    const tint::TypeInfo* type_info_ = nullptr;
+    const tint::utils::TypeInfo* type_info_ = nullptr;
 };
 
 /// Castable is a helper to derive `CLASS` from `BASE`, automatically implementing the Is() and As()
@@ -433,7 +433,7 @@
     /// @see CastFlags
     template <typename TO, int FLAGS = 0>
     inline bool Is() const {
-        return tint::Is<TO, FLAGS>(static_cast<const CLASS*>(this));
+        return tint::utils::Is<TO, FLAGS>(static_cast<const CLASS*>(this));
     }
 
     /// @returns true if this object is of, or derives from the class `TO` and
@@ -443,14 +443,15 @@
     template <int FLAGS = 0, typename Pred = detail::Infer>
     inline bool Is(Pred&& pred) const {
         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));
+        return tint::utils::Is<TO, FLAGS>(static_cast<const CLASS*>(this),
+                                          std::forward<Pred>(pred));
     }
 
     /// @returns true if this object is of, or derives from any of the `TO`
     /// classes.
     template <typename... TO>
     inline bool IsAnyOf() const {
-        return tint::IsAnyOf<TO...>(static_cast<const CLASS*>(this));
+        return tint::utils::IsAnyOf<TO...>(static_cast<const CLASS*>(this));
     }
 
     /// @returns this object dynamically cast to the type `TO` or `nullptr` if
@@ -458,7 +459,7 @@
     /// @see CastFlags
     template <typename TO, int FLAGS = 0>
     inline TO* As() {
-        return tint::As<TO, FLAGS>(this);
+        return tint::utils::As<TO, FLAGS>(this);
     }
 
     /// @returns this object dynamically cast to the type `TO` or `nullptr` if
@@ -466,7 +467,7 @@
     /// @see CastFlags
     template <typename TO, int FLAGS = 0>
     inline const TO* As() const {
-        return tint::As<const TO, FLAGS>(this);
+        return tint::utils::As<const TO, FLAGS>(this);
     }
 };
 
@@ -530,8 +531,15 @@
 template <typename... TYPES>
 using CastableCommonBase = detail::CastableCommonBase<TYPES...>;
 
+}  // namespace tint::utils
+
+namespace tint {
+
+using utils::As;
+using utils::Is;
+
 }  // namespace tint
 
 TINT_CASTABLE_POP_DISABLE_WARNINGS();
 
-#endif  // SRC_TINT_CASTABLE_H_
+#endif  // SRC_TINT_UTILS_CASTABLE_H_
diff --git a/src/tint/castable_test.cc b/src/tint/utils/castable_test.cc
similarity index 94%
rename from src/tint/castable_test.cc
rename to src/tint/utils/castable_test.cc
index 57bd904..419e506 100644
--- a/src/tint/castable_test.cc
+++ b/src/tint/utils/castable_test.cc
@@ -12,25 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/castable.h"
+#include "src/tint/utils/castable.h"
 
 #include <memory>
 #include <string>
 
 #include "gtest/gtest.h"
 
-namespace tint {
+namespace tint::utils {
 namespace {
 
-struct Animal : public tint::Castable<Animal> {};
-struct Amphibian : public tint::Castable<Amphibian, Animal> {};
-struct Mammal : public tint::Castable<Mammal, Animal> {};
-struct Reptile : public tint::Castable<Reptile, Animal> {};
-struct Frog : public tint::Castable<Frog, Amphibian> {};
-struct Bear : public tint::Castable<Bear, Mammal> {};
-struct Lizard : public tint::Castable<Lizard, Reptile> {};
-struct Gecko : public tint::Castable<Gecko, Lizard> {};
-struct Iguana : public tint::Castable<Iguana, Lizard> {};
+struct Animal : public tint::utils::Castable<Animal> {};
+struct Amphibian : public tint::utils::Castable<Amphibian, Animal> {};
+struct Mammal : public tint::utils::Castable<Mammal, Animal> {};
+struct Reptile : public tint::utils::Castable<Reptile, Animal> {};
+struct Frog : public tint::utils::Castable<Frog, Amphibian> {};
+struct Bear : public tint::utils::Castable<Bear, Mammal> {};
+struct Lizard : public tint::utils::Castable<Lizard, Reptile> {};
+struct Gecko : public tint::utils::Castable<Gecko, Lizard> {};
+struct Iguana : public tint::utils::Castable<Iguana, Lizard> {};
 
 TEST(CastableBase, Is) {
     std::unique_ptr<CastableBase> frog = std::make_unique<Frog>();
@@ -291,4 +291,4 @@
 TINT_INSTANTIATE_TYPEINFO(Lizard);
 TINT_INSTANTIATE_TYPEINFO(Gecko);
 
-}  // namespace tint
+}  // namespace tint::utils
diff --git a/src/tint/utils/slice.h b/src/tint/utils/slice.h
index 9737cad..ad4dbcf 100644
--- a/src/tint/utils/slice.h
+++ b/src/tint/utils/slice.h
@@ -18,8 +18,8 @@
 #include <cstdint>
 #include <iterator>
 
-#include "src/tint/castable.h"
 #include "src/tint/utils/bitcast.h"
+#include "src/tint/utils/castable.h"
 #include "src/tint/utils/traits.h"
 
 namespace tint::utils {
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index f2c59e4..cd3834f 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -91,7 +91,7 @@
 const char kTempNamePrefix[] = "tint_tmp";
 
 bool last_is_break(const ast::BlockStatement* stmts) {
-    return IsAnyOf<ast::BreakStatement>(stmts->Last());
+    return utils::IsAnyOf<ast::BreakStatement>(stmts->Last());
 }
 
 bool IsRelational(tint::ast::BinaryOp op) {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 8b9bde8..5cce6a1 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -330,7 +330,7 @@
         return false;
     }
 
-    const TypeInfo* last_kind = nullptr;
+    const utils::TypeInfo* last_kind = nullptr;
     size_t last_padding_line = 0;
 
     auto* mod = builder_.Sem().Module();
@@ -2794,7 +2794,7 @@
         return false;
     }
 
-    if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
+    if (!tint::utils::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
         line() << "break;";
     }
 
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index f86a144..df9ff4b 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -90,7 +90,7 @@
 namespace {
 
 bool last_is_break(const ast::BlockStatement* stmts) {
-    return IsAnyOf<ast::BreakStatement>(stmts->Last());
+    return utils::IsAnyOf<ast::BreakStatement>(stmts->Last());
 }
 
 void PrintF32(utils::StringStream& out, float value) {