Use 'final' specifier on leaf classes

Tint makes heavy use of RTTI via virtual methods. Give the compiler the
opportunity to optimize away some of these virtuals.

Bug: tint:1383
Change-Id: I28edfaa0a05bb1a9c506c61c0084542c0aeb37f0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/82745
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ast/alias.h b/src/tint/ast/alias.h
index b308fa5..c21b3e2 100644
--- a/src/tint/ast/alias.h
+++ b/src/tint/ast/alias.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A type alias type. Holds a name and pointer to another type.
-class Alias : public Castable<Alias, TypeDecl> {
+class Alias final : public Castable<Alias, TypeDecl> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/array.h b/src/tint/ast/array.h
index b4429f3..413e4d3 100644
--- a/src/tint/ast/array.h
+++ b/src/tint/ast/array.h
@@ -27,7 +27,7 @@
 class Expression;
 
 /// An array type. If size is zero then it is a runtime array.
-class Array : public Castable<Array, Type> {
+class Array final : public Castable<Array, Type> {
  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 0b9c06b..55e07b8 100644
--- a/src/tint/ast/assignment_statement.h
+++ b/src/tint/ast/assignment_statement.h
@@ -22,7 +22,8 @@
 namespace ast {
 
 /// An assignment statement
-class AssignmentStatement : public Castable<AssignmentStatement, Statement> {
+class AssignmentStatement final
+    : public Castable<AssignmentStatement, Statement> {
  public:
   /// Constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/atomic.h b/src/tint/ast/atomic.h
index cdf3374..d5e1b9b 100644
--- a/src/tint/ast/atomic.h
+++ b/src/tint/ast/atomic.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// An atomic type.
-class Atomic : public Castable<Atomic, Type> {
+class Atomic final : public Castable<Atomic, Type> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/binary_expression.h b/src/tint/ast/binary_expression.h
index 73b2909..bf3112a 100644
--- a/src/tint/ast/binary_expression.h
+++ b/src/tint/ast/binary_expression.h
@@ -44,7 +44,7 @@
 };
 
 /// An binary expression
-class BinaryExpression : public Castable<BinaryExpression, Expression> {
+class BinaryExpression final : public Castable<BinaryExpression, Expression> {
  public:
   /// Constructor
   /// @param program_id 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 5e669ec..8f3214d 100644
--- a/src/tint/ast/binding_attribute.h
+++ b/src/tint/ast/binding_attribute.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A binding attribute
-class BindingAttribute : public Castable<BindingAttribute, Attribute> {
+class BindingAttribute final : public 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 db98d87..e16ae22 100644
--- a/src/tint/ast/bitcast_expression.h
+++ b/src/tint/ast/bitcast_expression.h
@@ -24,7 +24,7 @@
 class Type;
 
 /// A bitcast expression
-class BitcastExpression : public Castable<BitcastExpression, Expression> {
+class BitcastExpression final : public Castable<BitcastExpression, Expression> {
  public:
   /// Constructor
   /// @param program_id 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 fe0eeb5..31b2ef1 100644
--- a/src/tint/ast/block_statement.h
+++ b/src/tint/ast/block_statement.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A block statement
-class BlockStatement : public Castable<BlockStatement, Statement> {
+class BlockStatement final : public Castable<BlockStatement, Statement> {
  public:
   /// Constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/bool.h b/src/tint/ast/bool.h
index 77ec064..fa326da 100644
--- a/src/tint/ast/bool.h
+++ b/src/tint/ast/bool.h
@@ -29,7 +29,7 @@
 namespace ast {
 
 /// A boolean type
-class Bool : public Castable<Bool, Type> {
+class Bool final : public Castable<Bool, Type> {
  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 819c892..421453e 100644
--- a/src/tint/ast/bool_literal_expression.h
+++ b/src/tint/ast/bool_literal_expression.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A boolean literal
-class BoolLiteralExpression
+class BoolLiteralExpression final
     : public Castable<BoolLiteralExpression, LiteralExpression> {
  public:
   /// Constructor
diff --git a/src/tint/ast/break_statement.h b/src/tint/ast/break_statement.h
index c329f18..cf50c74 100644
--- a/src/tint/ast/break_statement.h
+++ b/src/tint/ast/break_statement.h
@@ -21,7 +21,7 @@
 namespace ast {
 
 /// An break statement
-class BreakStatement : public Castable<BreakStatement, Statement> {
+class BreakStatement final : public 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 e4f3f36..4366739 100644
--- a/src/tint/ast/builtin_attribute.h
+++ b/src/tint/ast/builtin_attribute.h
@@ -24,7 +24,7 @@
 namespace ast {
 
 /// A builtin attribute
-class BuiltinAttribute : public Castable<BuiltinAttribute, Attribute> {
+class BuiltinAttribute final : public 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 21b1c4c..efb1841 100644
--- a/src/tint/ast/call_expression.h
+++ b/src/tint/ast/call_expression.h
@@ -29,7 +29,7 @@
 /// * sem::Builtin
 /// * sem::TypeConstructor
 /// * sem::TypeConversion
-class CallExpression : public Castable<CallExpression, Expression> {
+class CallExpression final : public Castable<CallExpression, Expression> {
  public:
   /// Constructor
   /// @param program_id 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 6445819..b9e0c4c 100644
--- a/src/tint/ast/call_statement.h
+++ b/src/tint/ast/call_statement.h
@@ -22,7 +22,7 @@
 namespace ast {
 
 /// A call expression
-class CallStatement : public Castable<CallStatement, Statement> {
+class CallStatement final : public Castable<CallStatement, Statement> {
  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 d557728..99fd81d 100644
--- a/src/tint/ast/case_statement.h
+++ b/src/tint/ast/case_statement.h
@@ -27,7 +27,7 @@
 using CaseSelectorList = std::vector<const IntLiteralExpression*>;
 
 /// A case statement
-class CaseStatement : public Castable<CaseStatement, Statement> {
+class CaseStatement final : public Castable<CaseStatement, 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 707ae5b..ab0b12e 100644
--- a/src/tint/ast/continue_statement.h
+++ b/src/tint/ast/continue_statement.h
@@ -21,7 +21,7 @@
 namespace ast {
 
 /// An continue statement
-class ContinueStatement : public Castable<ContinueStatement, Statement> {
+class ContinueStatement final : public Castable<ContinueStatement, Statement> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/depth_multisampled_texture.h b/src/tint/ast/depth_multisampled_texture.h
index a22b52d..95051a4 100644
--- a/src/tint/ast/depth_multisampled_texture.h
+++ b/src/tint/ast/depth_multisampled_texture.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A multisampled depth texture type.
-class DepthMultisampledTexture
+class DepthMultisampledTexture final
     : public Castable<DepthMultisampledTexture, Texture> {
  public:
   /// Constructor
diff --git a/src/tint/ast/depth_texture.h b/src/tint/ast/depth_texture.h
index 8efc5ed..62cd09d 100644
--- a/src/tint/ast/depth_texture.h
+++ b/src/tint/ast/depth_texture.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A depth texture type.
-class DepthTexture : public Castable<DepthTexture, Texture> {
+class DepthTexture final : public Castable<DepthTexture, Texture> {
  public:
   /// Constructor
   /// @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 ef75c62..d46edb8 100644
--- a/src/tint/ast/disable_validation_attribute.h
+++ b/src/tint/ast/disable_validation_attribute.h
@@ -52,7 +52,7 @@
 /// An internal attribute used to tell the validator to ignore specific
 /// violations. Typically generated by transforms that need to produce ASTs that
 /// would otherwise cause validation errors.
-class DisableValidationAttribute
+class DisableValidationAttribute final
     : public Castable<DisableValidationAttribute, InternalAttribute> {
  public:
   /// Constructor
diff --git a/src/tint/ast/discard_statement.h b/src/tint/ast/discard_statement.h
index a884594..7e4fcf1 100644
--- a/src/tint/ast/discard_statement.h
+++ b/src/tint/ast/discard_statement.h
@@ -21,7 +21,7 @@
 namespace ast {
 
 /// A discard statement
-class DiscardStatement : public Castable<DiscardStatement, Statement> {
+class DiscardStatement final : public Castable<DiscardStatement, Statement> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/else_statement.h b/src/tint/ast/else_statement.h
index 979b3b9..6f641c7 100644
--- a/src/tint/ast/else_statement.h
+++ b/src/tint/ast/else_statement.h
@@ -24,7 +24,7 @@
 namespace ast {
 
 /// An else statement
-class ElseStatement : public Castable<ElseStatement, Statement> {
+class ElseStatement final : public Castable<ElseStatement, Statement> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/external_texture.h b/src/tint/ast/external_texture.h
index 6fc638d..cf9f60d 100644
--- a/src/tint/ast/external_texture.h
+++ b/src/tint/ast/external_texture.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// An external texture type
-class ExternalTexture : public Castable<ExternalTexture, Texture> {
+class ExternalTexture final : public Castable<ExternalTexture, Texture> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/f32.h b/src/tint/ast/f32.h
index 92c7f51..58019c9 100644
--- a/src/tint/ast/f32.h
+++ b/src/tint/ast/f32.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A float 32 type
-class F32 : public Castable<F32, Type> {
+class F32 final : public Castable<F32, Type> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/fallthrough_statement.h b/src/tint/ast/fallthrough_statement.h
index d9501dd..f1cc716 100644
--- a/src/tint/ast/fallthrough_statement.h
+++ b/src/tint/ast/fallthrough_statement.h
@@ -21,7 +21,8 @@
 namespace ast {
 
 /// An fallthrough statement
-class FallthroughStatement : public Castable<FallthroughStatement, Statement> {
+class FallthroughStatement final
+    : public Castable<FallthroughStatement, Statement> {
  public:
   /// Constructor
   /// @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 e367389..ffa3c2f 100644
--- a/src/tint/ast/float_literal_expression.h
+++ b/src/tint/ast/float_literal_expression.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A float literal
-class FloatLiteralExpression
+class FloatLiteralExpression final
     : public Castable<FloatLiteralExpression, LiteralExpression> {
  public:
   /// Constructor
diff --git a/src/tint/ast/for_loop_statement.h b/src/tint/ast/for_loop_statement.h
index 8d15d6c..040ba52 100644
--- a/src/tint/ast/for_loop_statement.h
+++ b/src/tint/ast/for_loop_statement.h
@@ -23,7 +23,7 @@
 class Expression;
 
 /// A for loop statement
-class ForLoopStatement : public Castable<ForLoopStatement, Statement> {
+class ForLoopStatement final : public Castable<ForLoopStatement, Statement> {
  public:
   /// Constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/function.h b/src/tint/ast/function.h
index fd48e3f..10ad7a6 100644
--- a/src/tint/ast/function.h
+++ b/src/tint/ast/function.h
@@ -33,7 +33,7 @@
 namespace ast {
 
 /// A Function statement.
-class Function : public Castable<Function, Node> {
+class Function final : public Castable<Function, Node> {
  public:
   /// Create a function
   /// @param program_id 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 31d5007..be5c61e 100644
--- a/src/tint/ast/group_attribute.h
+++ b/src/tint/ast/group_attribute.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A group attribute
-class GroupAttribute : public Castable<GroupAttribute, Attribute> {
+class GroupAttribute final : public Castable<GroupAttribute, Attribute> {
  public:
   /// constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/i32.h b/src/tint/ast/i32.h
index 22c7e1b..335bc98 100644
--- a/src/tint/ast/i32.h
+++ b/src/tint/ast/i32.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A signed int 32 type.
-class I32 : public Castable<I32, Type> {
+class I32 final : public Castable<I32, Type> {
  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 d4ede06..7fbac0e 100644
--- a/src/tint/ast/id_attribute.h
+++ b/src/tint/ast/id_attribute.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// An id attribute for pipeline-overridable constants
-class IdAttribute : public Castable<IdAttribute, Attribute> {
+class IdAttribute final : public 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_expression.h b/src/tint/ast/identifier_expression.h
index 58d0140..6c3c8b3 100644
--- a/src/tint/ast/identifier_expression.h
+++ b/src/tint/ast/identifier_expression.h
@@ -21,7 +21,8 @@
 namespace ast {
 
 /// An identifier expression
-class IdentifierExpression : public Castable<IdentifierExpression, Expression> {
+class IdentifierExpression final
+    : public 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 3afe172..dae9fd7 100644
--- a/src/tint/ast/if_statement.h
+++ b/src/tint/ast/if_statement.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// An if statement
-class IfStatement : public Castable<IfStatement, Statement> {
+class IfStatement final : public Castable<IfStatement, 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 41dbcec..6fabada 100644
--- a/src/tint/ast/index_accessor_expression.h
+++ b/src/tint/ast/index_accessor_expression.h
@@ -21,7 +21,7 @@
 namespace ast {
 
 /// An index accessor expression
-class IndexAccessorExpression
+class IndexAccessorExpression final
     : public Castable<IndexAccessorExpression, Expression> {
  public:
   /// Constructor
diff --git a/src/tint/ast/interpolate_attribute.h b/src/tint/ast/interpolate_attribute.h
index d198ee7..ac3b49d 100644
--- a/src/tint/ast/interpolate_attribute.h
+++ b/src/tint/ast/interpolate_attribute.h
@@ -30,7 +30,8 @@
 enum class InterpolationSampling { kNone = -1, kCenter, kCentroid, kSample };
 
 /// An interpolate attribute
-class InterpolateAttribute : public Castable<InterpolateAttribute, Attribute> {
+class InterpolateAttribute final
+    : public 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 c427147..08a58e8 100644
--- a/src/tint/ast/invariant_attribute.h
+++ b/src/tint/ast/invariant_attribute.h
@@ -23,7 +23,8 @@
 namespace ast {
 
 /// The invariant attribute
-class InvariantAttribute : public Castable<InvariantAttribute, Attribute> {
+class InvariantAttribute final
+    : public Castable<InvariantAttribute, Attribute> {
  public:
   /// constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/location_attribute.h b/src/tint/ast/location_attribute.h
index 74a01cf..3c8078d 100644
--- a/src/tint/ast/location_attribute.h
+++ b/src/tint/ast/location_attribute.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A location attribute
-class LocationAttribute : public Castable<LocationAttribute, Attribute> {
+class LocationAttribute final : public 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 13484ed..be29fc0 100644
--- a/src/tint/ast/loop_statement.h
+++ b/src/tint/ast/loop_statement.h
@@ -21,7 +21,7 @@
 namespace ast {
 
 /// A loop statement
-class LoopStatement : public Castable<LoopStatement, Statement> {
+class LoopStatement final : public Castable<LoopStatement, Statement> {
  public:
   /// Constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/matrix.h b/src/tint/ast/matrix.h
index 6f96d4b..0ba418c 100644
--- a/src/tint/ast/matrix.h
+++ b/src/tint/ast/matrix.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A matrix type
-class Matrix : public Castable<Matrix, Type> {
+class Matrix final : public Castable<Matrix, Type> {
  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 9aa3e3a..8a82ba1 100644
--- a/src/tint/ast/member_accessor_expression.h
+++ b/src/tint/ast/member_accessor_expression.h
@@ -21,7 +21,7 @@
 namespace ast {
 
 /// A member accessor expression
-class MemberAccessorExpression
+class MemberAccessorExpression final
     : public Castable<MemberAccessorExpression, Expression> {
  public:
   /// Constructor
diff --git a/src/tint/ast/module.h b/src/tint/ast/module.h
index f7b3faf..b98013d 100644
--- a/src/tint/ast/module.h
+++ b/src/tint/ast/module.h
@@ -28,7 +28,7 @@
 
 /// Module holds the top-level AST types, functions and global variables used by
 /// a Program.
-class Module : public Castable<Module, Node> {
+class Module final : public Castable<Module, Node> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/multisampled_texture.h b/src/tint/ast/multisampled_texture.h
index ea91011..fbc3db0 100644
--- a/src/tint/ast/multisampled_texture.h
+++ b/src/tint/ast/multisampled_texture.h
@@ -23,7 +23,8 @@
 namespace ast {
 
 /// A multisampled texture type.
-class MultisampledTexture : public Castable<MultisampledTexture, Texture> {
+class MultisampledTexture final
+    : public Castable<MultisampledTexture, Texture> {
  public:
   /// Constructor
   /// @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 28f31ca..e021846 100644
--- a/src/tint/ast/phony_expression.h
+++ b/src/tint/ast/phony_expression.h
@@ -22,7 +22,7 @@
 
 /// Represents the `_` of a phony assignment `_ = <expr>`
 /// @see https://www.w3.org/TR/WGSL/#phony-assignment-section
-class PhonyExpression : public Castable<PhonyExpression, Expression> {
+class PhonyExpression final : public Castable<PhonyExpression, Expression> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/pointer.h b/src/tint/ast/pointer.h
index 38e2ef9..c73eb40 100644
--- a/src/tint/ast/pointer.h
+++ b/src/tint/ast/pointer.h
@@ -25,7 +25,7 @@
 namespace ast {
 
 /// A pointer type.
-class Pointer : public Castable<Pointer, Type> {
+class Pointer final : public Castable<Pointer, Type> {
  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 f6be148..5a20ff4 100644
--- a/src/tint/ast/return_statement.h
+++ b/src/tint/ast/return_statement.h
@@ -22,7 +22,7 @@
 namespace ast {
 
 /// A return statement
-class ReturnStatement : public Castable<ReturnStatement, Statement> {
+class ReturnStatement final : public Castable<ReturnStatement, Statement> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/sampled_texture.h b/src/tint/ast/sampled_texture.h
index af2cab3..6d0820c 100644
--- a/src/tint/ast/sampled_texture.h
+++ b/src/tint/ast/sampled_texture.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A sampled texture type.
-class SampledTexture : public Castable<SampledTexture, Texture> {
+class SampledTexture final : public Castable<SampledTexture, Texture> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/sampler.h b/src/tint/ast/sampler.h
index 7552e22..8724384 100644
--- a/src/tint/ast/sampler.h
+++ b/src/tint/ast/sampler.h
@@ -36,7 +36,7 @@
 std::ostream& operator<<(std::ostream& out, SamplerKind kind);
 
 /// A sampler type.
-class Sampler : public Castable<Sampler, Type> {
+class Sampler final : public Castable<Sampler, Type> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/sint_literal_expression.h b/src/tint/ast/sint_literal_expression.h
index bc0f60b..1431dd2 100644
--- a/src/tint/ast/sint_literal_expression.h
+++ b/src/tint/ast/sint_literal_expression.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A signed int literal
-class SintLiteralExpression
+class SintLiteralExpression final
     : public Castable<SintLiteralExpression, IntLiteralExpression> {
  public:
   /// Constructor
diff --git a/src/tint/ast/stage_attribute.h b/src/tint/ast/stage_attribute.h
index b523538..7bf918c 100644
--- a/src/tint/ast/stage_attribute.h
+++ b/src/tint/ast/stage_attribute.h
@@ -24,7 +24,7 @@
 namespace ast {
 
 /// A workgroup attribute
-class StageAttribute : public Castable<StageAttribute, Attribute> {
+class StageAttribute final : public Castable<StageAttribute, Attribute> {
  public:
   /// constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/storage_texture.h b/src/tint/ast/storage_texture.h
index e1f74d9..9af93af 100644
--- a/src/tint/ast/storage_texture.h
+++ b/src/tint/ast/storage_texture.h
@@ -50,7 +50,7 @@
 std::ostream& operator<<(std::ostream& out, TexelFormat format);
 
 /// A storage texture type.
-class StorageTexture : public Castable<StorageTexture, Texture> {
+class StorageTexture final : public Castable<StorageTexture, Texture> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/stride_attribute.h b/src/tint/ast/stride_attribute.h
index f2067cb..4edc54c 100644
--- a/src/tint/ast/stride_attribute.h
+++ b/src/tint/ast/stride_attribute.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A stride attribute
-class StrideAttribute : public Castable<StrideAttribute, Attribute> {
+class StrideAttribute final : public 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 95bc376..076c46d 100644
--- a/src/tint/ast/struct.h
+++ b/src/tint/ast/struct.h
@@ -26,7 +26,7 @@
 namespace ast {
 
 /// A struct statement.
-class Struct : public Castable<Struct, TypeDecl> {
+class Struct final : public 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_block_attribute.h b/src/tint/ast/struct_block_attribute.h
index df929f8..ba5c38f 100644
--- a/src/tint/ast/struct_block_attribute.h
+++ b/src/tint/ast/struct_block_attribute.h
@@ -24,7 +24,8 @@
 namespace ast {
 
 /// The struct block attribute
-class StructBlockAttribute : public Castable<StructBlockAttribute, Attribute> {
+class StructBlockAttribute final
+    : public Castable<StructBlockAttribute, Attribute> {
  public:
   /// Constructor
   /// @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 cf2e47b..4be9256 100644
--- a/src/tint/ast/struct_member.h
+++ b/src/tint/ast/struct_member.h
@@ -27,7 +27,7 @@
 class Type;
 
 /// A struct member statement.
-class StructMember : public Castable<StructMember, Node> {
+class StructMember final : public 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 b90c71f..a1b455b 100644
--- a/src/tint/ast/struct_member_align_attribute.h
+++ b/src/tint/ast/struct_member_align_attribute.h
@@ -24,7 +24,7 @@
 namespace ast {
 
 /// A struct member align attribute
-class StructMemberAlignAttribute
+class StructMemberAlignAttribute final
     : public Castable<StructMemberAlignAttribute, Attribute> {
  public:
   /// constructor
diff --git a/src/tint/ast/struct_member_offset_attribute.h b/src/tint/ast/struct_member_offset_attribute.h
index f599a7e..63db959 100644
--- a/src/tint/ast/struct_member_offset_attribute.h
+++ b/src/tint/ast/struct_member_offset_attribute.h
@@ -32,7 +32,7 @@
 /// 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
+class StructMemberOffsetAttribute final
     : public Castable<StructMemberOffsetAttribute, Attribute> {
  public:
   /// constructor
diff --git a/src/tint/ast/struct_member_size_attribute.h b/src/tint/ast/struct_member_size_attribute.h
index a6397ab..c2a6e50 100644
--- a/src/tint/ast/struct_member_size_attribute.h
+++ b/src/tint/ast/struct_member_size_attribute.h
@@ -24,7 +24,7 @@
 namespace ast {
 
 /// A struct member size attribute
-class StructMemberSizeAttribute
+class StructMemberSizeAttribute final
     : public Castable<StructMemberSizeAttribute, Attribute> {
  public:
   /// constructor
diff --git a/src/tint/ast/switch_statement.h b/src/tint/ast/switch_statement.h
index 8fa2fb5..aa0cc77 100644
--- a/src/tint/ast/switch_statement.h
+++ b/src/tint/ast/switch_statement.h
@@ -22,7 +22,7 @@
 namespace ast {
 
 /// A switch statement
-class SwitchStatement : public Castable<SwitchStatement, Statement> {
+class SwitchStatement final : public Castable<SwitchStatement, Statement> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/type_name.h b/src/tint/ast/type_name.h
index c8e85da..9bae5d0 100644
--- a/src/tint/ast/type_name.h
+++ b/src/tint/ast/type_name.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A named type (i.e. struct or alias)
-class TypeName : public Castable<TypeName, Type> {
+class TypeName final : public Castable<TypeName, Type> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/u32.h b/src/tint/ast/u32.h
index 156fbe2..a31222b 100644
--- a/src/tint/ast/u32.h
+++ b/src/tint/ast/u32.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A unsigned int 32 type.
-class U32 : public Castable<U32, Type> {
+class U32 final : public Castable<U32, Type> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/uint_literal_expression.h b/src/tint/ast/uint_literal_expression.h
index edc79e1..58fdb96 100644
--- a/src/tint/ast/uint_literal_expression.h
+++ b/src/tint/ast/uint_literal_expression.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A uint literal
-class UintLiteralExpression
+class UintLiteralExpression final
     : public Castable<UintLiteralExpression, IntLiteralExpression> {
  public:
   /// Constructor
diff --git a/src/tint/ast/unary_op_expression.h b/src/tint/ast/unary_op_expression.h
index 62bc881..9b19a77 100644
--- a/src/tint/ast/unary_op_expression.h
+++ b/src/tint/ast/unary_op_expression.h
@@ -22,7 +22,7 @@
 namespace ast {
 
 /// A unary op expression
-class UnaryOpExpression : public Castable<UnaryOpExpression, Expression> {
+class UnaryOpExpression final : public Castable<UnaryOpExpression, Expression> {
  public:
   /// Constructor
   /// @param program_id the identifier of the program that owns this node
diff --git a/src/tint/ast/variable.h b/src/tint/ast/variable.h
index 0afe376..88b6ad2 100644
--- a/src/tint/ast/variable.h
+++ b/src/tint/ast/variable.h
@@ -113,7 +113,7 @@
 ///     defaulting syntax for a "var" declared inside a function.
 ///   - "let" is always StorageClass::kNone.
 ///   - formal parameter is always StorageClass::kNone.
-class Variable : public Castable<Variable, Node> {
+class Variable final : public Castable<Variable, Node> {
  public:
   /// Create a variable
   /// @param program_id 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 3419d37..47fb8ab 100644
--- a/src/tint/ast/variable_decl_statement.h
+++ b/src/tint/ast/variable_decl_statement.h
@@ -22,7 +22,7 @@
 namespace ast {
 
 /// A variable declaration statement
-class VariableDeclStatement
+class VariableDeclStatement final
     : public Castable<VariableDeclStatement, Statement> {
  public:
   /// Constructor
diff --git a/src/tint/ast/vector.h b/src/tint/ast/vector.h
index 4087817..bfc4908 100644
--- a/src/tint/ast/vector.h
+++ b/src/tint/ast/vector.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A vector type.
-class Vector : public Castable<Vector, Type> {
+class Vector final : public Castable<Vector, Type> {
  public:
   /// Constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/ast/void.h b/src/tint/ast/void.h
index 69d1571..94d382f 100644
--- a/src/tint/ast/void.h
+++ b/src/tint/ast/void.h
@@ -23,7 +23,7 @@
 namespace ast {
 
 /// A void type
-class Void : public Castable<Void, Type> {
+class Void final : public Castable<Void, Type> {
  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 815420a..5ffc7c6 100644
--- a/src/tint/ast/workgroup_attribute.h
+++ b/src/tint/ast/workgroup_attribute.h
@@ -27,7 +27,8 @@
 class Expression;
 
 /// A workgroup attribute
-class WorkgroupAttribute : public Castable<WorkgroupAttribute, Attribute> {
+class WorkgroupAttribute final
+    : public Castable<WorkgroupAttribute, Attribute> {
  public:
   /// constructor
   /// @param pid the identifier of the program that owns this node
diff --git a/src/tint/builtin_table.cc b/src/tint/builtin_table.cc
index c7443fd..13e7c5f 100644
--- a/src/tint/builtin_table.cc
+++ b/src/tint/builtin_table.cc
@@ -43,7 +43,7 @@
 class TypeMatcher;
 
 /// A special type that matches all TypeMatchers
-class Any : public Castable<Any, sem::Type> {
+class Any final : public Castable<Any, sem::Type> {
  public:
   Any() = default;
   ~Any() override = default;
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 9c16094..c130e85 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -647,7 +647,7 @@
 
 /// A StatementBuilder for ast::SwitchStatement
 /// @see StatementBuilder
-struct SwitchStatementBuilder
+struct SwitchStatementBuilder final
     : public Castable<SwitchStatementBuilder, StatementBuilder> {
   /// Constructor
   /// @param cond the switch statement condition
@@ -674,7 +674,7 @@
 
 /// A StatementBuilder for ast::IfStatement
 /// @see StatementBuilder
-struct IfStatementBuilder
+struct IfStatementBuilder final
     : public Castable<IfStatementBuilder, StatementBuilder> {
   /// Constructor
   /// @param c the if-statement condition
@@ -696,7 +696,7 @@
 
 /// A StatementBuilder for ast::LoopStatement
 /// @see StatementBuilder
-struct LoopStatementBuilder
+struct LoopStatementBuilder final
     : public Castable<LoopStatementBuilder, StatementBuilder> {
   /// @param builder the program builder
   /// @returns the built ast::LoopStatement
diff --git a/src/tint/reader/spirv/parser_type.h b/src/tint/reader/spirv/parser_type.h
index c550ad0..2313e9e 100644
--- a/src/tint/reader/spirv/parser_type.h
+++ b/src/tint/reader/spirv/parser_type.h
@@ -92,7 +92,7 @@
 using TypeList = std::vector<const Type*>;
 
 /// `void` type
-struct Void : public Castable<Void, Type> {
+struct Void final : public Castable<Void, Type> {
   /// @param b the ProgramBuilder used to construct the AST types
   /// @returns the constructed ast::Type node for the given type
   const ast::Type* Build(ProgramBuilder& b) const override;
@@ -104,7 +104,7 @@
 };
 
 /// `bool` type
-struct Bool : public Castable<Bool, Type> {
+struct Bool final : public Castable<Bool, Type> {
   /// @param b the ProgramBuilder used to construct the AST types
   /// @returns the constructed ast::Type node for the given type
   const ast::Type* Build(ProgramBuilder& b) const override;
@@ -116,7 +116,7 @@
 };
 
 /// `u32` type
-struct U32 : public Castable<U32, Type> {
+struct U32 final : public Castable<U32, Type> {
   /// @param b the ProgramBuilder used to construct the AST types
   /// @returns the constructed ast::Type node for the given type
   const ast::Type* Build(ProgramBuilder& b) const override;
@@ -128,7 +128,7 @@
 };
 
 /// `f32` type
-struct F32 : public Castable<F32, Type> {
+struct F32 final : public Castable<F32, Type> {
   /// @param b the ProgramBuilder used to construct the AST types
   /// @returns the constructed ast::Type node for the given type
   const ast::Type* Build(ProgramBuilder& b) const override;
@@ -140,7 +140,7 @@
 };
 
 /// `i32` type
-struct I32 : public Castable<I32, Type> {
+struct I32 final : public Castable<I32, Type> {
   /// @param b the ProgramBuilder used to construct the AST types
   /// @returns the constructed ast::Type node for the given type
   const ast::Type* Build(ProgramBuilder& b) const override;
@@ -152,7 +152,7 @@
 };
 
 /// `ptr<SC, T>` type
-struct Pointer : public Castable<Pointer, Type> {
+struct Pointer final : public Castable<Pointer, Type> {
   /// Constructor
   /// @param ty the store type
   /// @param sc the pointer storage class
@@ -180,7 +180,7 @@
 /// `ref<SC, T>` type
 /// Note this has no AST representation, but is used for type tracking in the
 /// reader.
-struct Reference : public Castable<Reference, Type> {
+struct Reference final : public Castable<Reference, Type> {
   /// Constructor
   /// @param ty the referenced type
   /// @param sc the reference storage class
@@ -206,7 +206,7 @@
 };
 
 /// `vecN<T>` type
-struct Vector : public Castable<Vector, Type> {
+struct Vector final : public Castable<Vector, Type> {
   /// Constructor
   /// @param ty the element type
   /// @param sz the number of elements in the vector
@@ -232,7 +232,7 @@
 };
 
 /// `matNxM<T>` type
-struct Matrix : public Castable<Matrix, Type> {
+struct Matrix final : public Castable<Matrix, Type> {
   /// Constructor
   /// @param ty the matrix element type
   /// @param c the number of columns in the matrix
@@ -261,7 +261,7 @@
 };
 
 /// `array<T, N>` type
-struct Array : public Castable<Array, Type> {
+struct Array final : public Castable<Array, Type> {
   /// Constructor
   /// @param el the element type
   /// @param sz the number of elements in the array. 0 represents runtime-sized
@@ -291,7 +291,7 @@
 };
 
 /// `sampler` type
-struct Sampler : public Castable<Sampler, Type> {
+struct Sampler final : public Castable<Sampler, Type> {
   /// Constructor
   /// @param k the sampler kind
   explicit Sampler(ast::SamplerKind k);
@@ -328,7 +328,7 @@
 };
 
 /// `texture_depth_D` type
-struct DepthTexture : public Castable<DepthTexture, Texture> {
+struct DepthTexture final : public Castable<DepthTexture, Texture> {
   /// Constructor
   /// @param d the texture dimensions
   explicit DepthTexture(ast::TextureDimension d);
@@ -348,7 +348,7 @@
 };
 
 /// `texture_depth_multisampled_D` type
-struct DepthMultisampledTexture
+struct DepthMultisampledTexture final
     : public Castable<DepthMultisampledTexture, Texture> {
   /// Constructor
   /// @param d the texture dimensions
@@ -369,7 +369,8 @@
 };
 
 /// `texture_multisampled_D<T>` type
-struct MultisampledTexture : public Castable<MultisampledTexture, Texture> {
+struct MultisampledTexture final
+    : public Castable<MultisampledTexture, Texture> {
   /// Constructor
   /// @param d the texture dimensions
   /// @param t the multisampled texture type
@@ -393,7 +394,7 @@
 };
 
 /// `texture_D<T>` type
-struct SampledTexture : public Castable<SampledTexture, Texture> {
+struct SampledTexture final : public Castable<SampledTexture, Texture> {
   /// Constructor
   /// @param d the texture dimensions
   /// @param t the sampled texture type
@@ -417,7 +418,7 @@
 };
 
 /// `texture_storage_D<F>` type
-struct StorageTexture : public Castable<StorageTexture, Texture> {
+struct StorageTexture final : public Castable<StorageTexture, Texture> {
   /// Constructor
   /// @param d the texture dimensions
   /// @param f the storage image format
@@ -467,7 +468,7 @@
 };
 
 /// `type T = N` type
-struct Alias : public Castable<Alias, Named> {
+struct Alias final : public Castable<Alias, Named> {
   /// Constructor
   /// @param n the alias name
   /// @param t the aliased type
@@ -486,7 +487,7 @@
 };
 
 /// `struct N { ... };` type
-struct Struct : public Castable<Struct, Named> {
+struct Struct final : public Castable<Struct, Named> {
   /// Constructor
   /// @param n the struct name
   /// @param m the member types
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 9198487..dbf8115 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -47,13 +47,13 @@
 
 using ResolverValidationTest = ResolverTest;
 
-class FakeStmt : public Castable<FakeStmt, ast::Statement> {
+class FakeStmt final : public Castable<FakeStmt, ast::Statement> {
  public:
   FakeStmt(ProgramID pid, Source src) : Base(pid, src) {}
   FakeStmt* Clone(CloneContext*) const override { return nullptr; }
 };
 
-class FakeExpr : public Castable<FakeExpr, ast::Expression> {
+class FakeExpr final : public Castable<FakeExpr, ast::Expression> {
  public:
   FakeExpr(ProgramID pid, Source src) : Base(pid, src) {}
   FakeExpr* Clone(CloneContext*) const override { return nullptr; }
diff --git a/src/tint/sem/array.h b/src/tint/sem/array.h
index 573e004..b6639fe 100644
--- a/src/tint/sem/array.h
+++ b/src/tint/sem/array.h
@@ -32,7 +32,7 @@
 namespace sem {
 
 /// Array holds the semantic information for Array nodes.
-class Array : public Castable<Array, Type> {
+class Array final : public Castable<Array, Type> {
  public:
   /// Constructor
   /// @param element the array element type
diff --git a/src/tint/sem/atomic_type.h b/src/tint/sem/atomic_type.h
index e8c7a4d..20035ec 100644
--- a/src/tint/sem/atomic_type.h
+++ b/src/tint/sem/atomic_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A atomic type.
-class Atomic : public Castable<Atomic, Type> {
+class Atomic final : public Castable<Atomic, Type> {
  public:
   /// Constructor
   /// @param subtype the atomic type
diff --git a/src/tint/sem/block_statement.h b/src/tint/sem/block_statement.h
index 2b54447..c6e537f 100644
--- a/src/tint/sem/block_statement.h
+++ b/src/tint/sem/block_statement.h
@@ -64,7 +64,7 @@
 };
 
 /// The root block statement for a function
-class FunctionBlockStatement
+class FunctionBlockStatement final
     : public Castable<FunctionBlockStatement, BlockStatement> {
  public:
   /// Constructor
@@ -76,7 +76,8 @@
 };
 
 /// Holds semantic information about a loop body block or for-loop body block
-class LoopBlockStatement : public Castable<LoopBlockStatement, BlockStatement> {
+class LoopBlockStatement final
+    : public Castable<LoopBlockStatement, BlockStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this block statement
diff --git a/src/tint/sem/bool_type.h b/src/tint/sem/bool_type.h
index 404fadc..7e53e88 100644
--- a/src/tint/sem/bool_type.h
+++ b/src/tint/sem/bool_type.h
@@ -29,7 +29,7 @@
 namespace sem {
 
 /// A boolean type
-class Bool : public Castable<Bool, Type> {
+class Bool final : public Castable<Bool, Type> {
  public:
   /// Constructor
   Bool();
diff --git a/src/tint/sem/builtin.h b/src/tint/sem/builtin.h
index 533a30b..bb216ac 100644
--- a/src/tint/sem/builtin.h
+++ b/src/tint/sem/builtin.h
@@ -72,7 +72,7 @@
 bool IsAtomicBuiltin(BuiltinType i);
 
 /// Builtin holds the semantic information for a builtin function.
-class Builtin : public Castable<Builtin, CallTarget> {
+class Builtin final : public Castable<Builtin, CallTarget> {
  public:
   /// Constructor
   /// @param type the builtin type
diff --git a/src/tint/sem/call.h b/src/tint/sem/call.h
index c967445..fad6db3 100644
--- a/src/tint/sem/call.h
+++ b/src/tint/sem/call.h
@@ -25,7 +25,7 @@
 
 /// Call is the base class for semantic nodes that hold semantic information for
 /// ast::CallExpression nodes.
-class Call : public Castable<Call, Expression> {
+class Call final : public Castable<Call, Expression> {
  public:
   /// Constructor
   /// @param declaration the AST node
diff --git a/src/tint/sem/depth_multisampled_texture_type.h b/src/tint/sem/depth_multisampled_texture_type.h
index 539fe0e..f2eff66 100644
--- a/src/tint/sem/depth_multisampled_texture_type.h
+++ b/src/tint/sem/depth_multisampled_texture_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A multisampled depth texture type.
-class DepthMultisampledTexture
+class DepthMultisampledTexture final
     : public Castable<DepthMultisampledTexture, Texture> {
  public:
   /// Constructor
diff --git a/src/tint/sem/depth_texture_type.h b/src/tint/sem/depth_texture_type.h
index 45ca46f..657c4b6 100644
--- a/src/tint/sem/depth_texture_type.h
+++ b/src/tint/sem/depth_texture_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A depth texture type.
-class DepthTexture : public Castable<DepthTexture, Texture> {
+class DepthTexture final : public Castable<DepthTexture, Texture> {
  public:
   /// Constructor
   /// @param dim the dimensionality of the texture
diff --git a/src/tint/sem/external_texture_type.h b/src/tint/sem/external_texture_type.h
index 197cbed..2406c08 100644
--- a/src/tint/sem/external_texture_type.h
+++ b/src/tint/sem/external_texture_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// An external texture type
-class ExternalTexture : public Castable<ExternalTexture, Texture> {
+class ExternalTexture final : public Castable<ExternalTexture, Texture> {
  public:
   /// Constructor
   ExternalTexture();
diff --git a/src/tint/sem/f32_type.h b/src/tint/sem/f32_type.h
index ff5872b..c239c16 100644
--- a/src/tint/sem/f32_type.h
+++ b/src/tint/sem/f32_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A float 32 type
-class F32 : public Castable<F32, Type> {
+class F32 final : public Castable<F32, Type> {
  public:
   /// Constructor
   F32();
diff --git a/src/tint/sem/for_loop_statement.h b/src/tint/sem/for_loop_statement.h
index 8681089..7f8321b 100644
--- a/src/tint/sem/for_loop_statement.h
+++ b/src/tint/sem/for_loop_statement.h
@@ -30,7 +30,8 @@
 namespace sem {
 
 /// Holds semantic information about a for-loop statement
-class ForLoopStatement : public Castable<ForLoopStatement, CompoundStatement> {
+class ForLoopStatement final
+    : public Castable<ForLoopStatement, CompoundStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this for-loop statement
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index 90657e7..6e34356 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -52,7 +52,7 @@
 using WorkgroupSize = std::array<WorkgroupDimension, 3>;
 
 /// Function holds the semantic information for function nodes.
-class Function : public Castable<Function, CallTarget> {
+class Function final : public Castable<Function, CallTarget> {
  public:
   /// A vector of [Variable*, ast::VariableBindingPoint] pairs
   using VariableBindings =
diff --git a/src/tint/sem/i32_type.h b/src/tint/sem/i32_type.h
index 47036ef..6a28f8f 100644
--- a/src/tint/sem/i32_type.h
+++ b/src/tint/sem/i32_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A signed int 32 type.
-class I32 : public Castable<I32, Type> {
+class I32 final : public Castable<I32, Type> {
  public:
   /// Constructor
   I32();
diff --git a/src/tint/sem/if_statement.h b/src/tint/sem/if_statement.h
index eb6b6a4..7dc2a11 100644
--- a/src/tint/sem/if_statement.h
+++ b/src/tint/sem/if_statement.h
@@ -32,7 +32,7 @@
 namespace sem {
 
 /// Holds semantic information about an if statement
-class IfStatement : public Castable<IfStatement, CompoundStatement> {
+class IfStatement final : public Castable<IfStatement, CompoundStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this if statement
@@ -60,7 +60,7 @@
 };
 
 /// Holds semantic information about an else statement
-class ElseStatement : public Castable<ElseStatement, CompoundStatement> {
+class ElseStatement final : public Castable<ElseStatement, CompoundStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this else statement
diff --git a/src/tint/sem/loop_statement.h b/src/tint/sem/loop_statement.h
index 7168c31..23ca4d3 100644
--- a/src/tint/sem/loop_statement.h
+++ b/src/tint/sem/loop_statement.h
@@ -28,7 +28,7 @@
 namespace sem {
 
 /// Holds semantic information about a loop statement
-class LoopStatement : public Castable<LoopStatement, CompoundStatement> {
+class LoopStatement final : public Castable<LoopStatement, CompoundStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this loop statement
@@ -43,7 +43,7 @@
 };
 
 /// Holds semantic information about a loop continuing block
-class LoopContinuingBlockStatement
+class LoopContinuingBlockStatement final
     : public Castable<LoopContinuingBlockStatement, BlockStatement> {
  public:
   /// Constructor
diff --git a/src/tint/sem/matrix_type.h b/src/tint/sem/matrix_type.h
index 67f8fe8..888a7af 100644
--- a/src/tint/sem/matrix_type.h
+++ b/src/tint/sem/matrix_type.h
@@ -26,7 +26,7 @@
 class Vector;
 
 /// A matrix type
-class Matrix : public Castable<Matrix, Type> {
+class Matrix final : public Castable<Matrix, Type> {
  public:
   /// Constructor
   /// @param column_type the type of a column of the matrix
diff --git a/src/tint/sem/member_accessor_expression.h b/src/tint/sem/member_accessor_expression.h
index 7a1a2a3..df49504 100644
--- a/src/tint/sem/member_accessor_expression.h
+++ b/src/tint/sem/member_accessor_expression.h
@@ -54,7 +54,7 @@
 /// StructMemberAccess holds the semantic information for a
 /// ast::MemberAccessorExpression node that represents an access to a structure
 /// member.
-class StructMemberAccess
+class StructMemberAccess final
     : public Castable<StructMemberAccess, MemberAccessorExpression> {
  public:
   /// Constructor
@@ -81,7 +81,7 @@
 
 /// Swizzle holds the semantic information for a ast::MemberAccessorExpression
 /// node that represents a vector swizzle.
-class Swizzle : public Castable<Swizzle, MemberAccessorExpression> {
+class Swizzle final : public 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 a21579e..1077b7e 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 : public Castable<Module, Node> {
+class Module final : public Castable<Module, Node> {
  public:
   /// Constructor
   /// @param dep_ordered_decls the dependency-ordered module-scope declarations
diff --git a/src/tint/sem/multisampled_texture_type.h b/src/tint/sem/multisampled_texture_type.h
index 4272e71..1e1ee05 100644
--- a/src/tint/sem/multisampled_texture_type.h
+++ b/src/tint/sem/multisampled_texture_type.h
@@ -23,7 +23,8 @@
 namespace sem {
 
 /// A multisampled texture type.
-class MultisampledTexture : public Castable<MultisampledTexture, Texture> {
+class MultisampledTexture final
+    : public Castable<MultisampledTexture, Texture> {
  public:
   /// Constructor
   /// @param dim the dimensionality of the texture
diff --git a/src/tint/sem/pointer_type.h b/src/tint/sem/pointer_type.h
index d652cea..7e2e48c 100644
--- a/src/tint/sem/pointer_type.h
+++ b/src/tint/sem/pointer_type.h
@@ -25,7 +25,7 @@
 namespace sem {
 
 /// A pointer type.
-class Pointer : public Castable<Pointer, Type> {
+class Pointer final : public Castable<Pointer, Type> {
  public:
   /// Constructor
   /// @param subtype the pointee type
diff --git a/src/tint/sem/reference_type.h b/src/tint/sem/reference_type.h
index 4628b1c..cc2921b 100644
--- a/src/tint/sem/reference_type.h
+++ b/src/tint/sem/reference_type.h
@@ -25,7 +25,7 @@
 namespace sem {
 
 /// A reference type.
-class Reference : public Castable<Reference, Type> {
+class Reference final : public Castable<Reference, Type> {
  public:
   /// Constructor
   /// @param subtype the pointee type
diff --git a/src/tint/sem/sampled_texture_type.h b/src/tint/sem/sampled_texture_type.h
index 79c39ae..4cfa15c 100644
--- a/src/tint/sem/sampled_texture_type.h
+++ b/src/tint/sem/sampled_texture_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A sampled texture type.
-class SampledTexture : public Castable<SampledTexture, Texture> {
+class SampledTexture final : public Castable<SampledTexture, Texture> {
  public:
   /// Constructor
   /// @param dim the dimensionality of the texture
diff --git a/src/tint/sem/sampler_type.h b/src/tint/sem/sampler_type.h
index f565e77..fd11fb5 100644
--- a/src/tint/sem/sampler_type.h
+++ b/src/tint/sem/sampler_type.h
@@ -24,7 +24,7 @@
 namespace sem {
 
 /// A sampler type.
-class Sampler : public Castable<Sampler, Type> {
+class Sampler final : public Castable<Sampler, Type> {
  public:
   /// Constructor
   /// @param kind the kind of sampler
diff --git a/src/tint/sem/storage_texture_type.h b/src/tint/sem/storage_texture_type.h
index 4785680..f3d5459 100644
--- a/src/tint/sem/storage_texture_type.h
+++ b/src/tint/sem/storage_texture_type.h
@@ -27,7 +27,7 @@
 class Manager;
 
 /// A storage texture type.
-class StorageTexture : public Castable<StorageTexture, Texture> {
+class StorageTexture final : public Castable<StorageTexture, Texture> {
  public:
   /// Constructor
   /// @param dim the dimensionality of the texture
diff --git a/src/tint/sem/struct.h b/src/tint/sem/struct.h
index 7748ee2..dc33dc2 100644
--- a/src/tint/sem/struct.h
+++ b/src/tint/sem/struct.h
@@ -54,7 +54,7 @@
 };
 
 /// Struct holds the semantic information for structures.
-class Struct : public Castable<Struct, Type> {
+class Struct final : public Castable<Struct, Type> {
  public:
   /// Constructor
   /// @param declaration the AST structure declaration
diff --git a/src/tint/sem/switch_statement.h b/src/tint/sem/switch_statement.h
index ab9e4eb..ff17f58 100644
--- a/src/tint/sem/switch_statement.h
+++ b/src/tint/sem/switch_statement.h
@@ -29,7 +29,8 @@
 namespace sem {
 
 /// Holds semantic information about an switch statement
-class SwitchStatement : public Castable<SwitchStatement, CompoundStatement> {
+class SwitchStatement final
+    : public Castable<SwitchStatement, CompoundStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this switch statement
@@ -47,7 +48,7 @@
 };
 
 /// Holds semantic information about a switch case statement
-class CaseStatement : public Castable<CaseStatement, CompoundStatement> {
+class CaseStatement final : public Castable<CaseStatement, CompoundStatement> {
  public:
   /// Constructor
   /// @param declaration the AST node for this case statement
diff --git a/src/tint/sem/type_constructor.h b/src/tint/sem/type_constructor.h
index 6126949..ca6bf81 100644
--- a/src/tint/sem/type_constructor.h
+++ b/src/tint/sem/type_constructor.h
@@ -21,7 +21,7 @@
 namespace sem {
 
 /// TypeConstructor is the CallTarget for a type constructor.
-class TypeConstructor : public Castable<TypeConstructor, CallTarget> {
+class TypeConstructor final : public Castable<TypeConstructor, CallTarget> {
  public:
   /// Constructor
   /// @param type the type that's being constructed
diff --git a/src/tint/sem/type_conversion.h b/src/tint/sem/type_conversion.h
index ae67711..fcea914 100644
--- a/src/tint/sem/type_conversion.h
+++ b/src/tint/sem/type_conversion.h
@@ -21,7 +21,7 @@
 namespace sem {
 
 /// TypeConversion is the CallTarget for a type conversion (cast).
-class TypeConversion : public Castable<TypeConversion, CallTarget> {
+class TypeConversion final : public Castable<TypeConversion, CallTarget> {
  public:
   /// Constructor
   /// @param type the target type of the cast
diff --git a/src/tint/sem/type_manager.h b/src/tint/sem/type_manager.h
index 50f5f2d..6d68af0 100644
--- a/src/tint/sem/type_manager.h
+++ b/src/tint/sem/type_manager.h
@@ -25,7 +25,7 @@
 namespace tint::sem {
 
 /// The type manager holds all the pointers to the known types.
-class Manager : public utils::UniqueAllocator<Type> {
+class Manager final : public utils::UniqueAllocator<Type> {
  public:
   /// Iterator is the type returned by begin() and end()
   using Iterator = utils::BlockAllocator<Type>::ConstIterator;
diff --git a/src/tint/sem/u32_type.h b/src/tint/sem/u32_type.h
index 83a36f5..5fa5c12 100644
--- a/src/tint/sem/u32_type.h
+++ b/src/tint/sem/u32_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A unsigned int 32 type.
-class U32 : public Castable<U32, Type> {
+class U32 final : public Castable<U32, Type> {
  public:
   /// Constructor
   U32();
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index b2f29dd..0f196d0 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -100,7 +100,7 @@
 };
 
 /// LocalVariable is a function-scope variable
-class LocalVariable : public Castable<LocalVariable, Variable> {
+class LocalVariable final : public Castable<LocalVariable, Variable> {
  public:
   /// Constructor
   /// @param declaration the AST declaration node
@@ -135,7 +135,7 @@
 };
 
 /// GlobalVariable is a module-scope variable
-class GlobalVariable : public Castable<GlobalVariable, Variable> {
+class GlobalVariable final : public Castable<GlobalVariable, Variable> {
  public:
   /// Constructor
   /// @param declaration the AST declaration node
@@ -182,7 +182,7 @@
 };
 
 /// Parameter is a function parameter
-class Parameter : public Castable<Parameter, Variable> {
+class Parameter final : public Castable<Parameter, Variable> {
  public:
   /// Constructor for function parameters
   /// @param declaration the AST declaration node
@@ -232,7 +232,7 @@
 
 /// VariableUser holds the semantic information for an identifier expression
 /// node that resolves to a variable.
-class VariableUser : public Castable<VariableUser, Expression> {
+class VariableUser final : public Castable<VariableUser, Expression> {
  public:
   /// Constructor
   /// @param declaration the AST identifier node
diff --git a/src/tint/sem/vector_type.h b/src/tint/sem/vector_type.h
index 38c2a8d..48ee1c5 100644
--- a/src/tint/sem/vector_type.h
+++ b/src/tint/sem/vector_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A vector type.
-class Vector : public Castable<Vector, Type> {
+class Vector final : public Castable<Vector, Type> {
  public:
   /// Constructor
   /// @param subtype the vector element type
diff --git a/src/tint/sem/void_type.h b/src/tint/sem/void_type.h
index d6cabfb..0411de0 100644
--- a/src/tint/sem/void_type.h
+++ b/src/tint/sem/void_type.h
@@ -23,7 +23,7 @@
 namespace sem {
 
 /// A void type
-class Void : public Castable<Void, Type> {
+class Void final : public Castable<Void, Type> {
  public:
   /// Constructor
   Void();
diff --git a/src/tint/transform/add_empty_entry_point.h b/src/tint/transform/add_empty_entry_point.h
index 36e250e..33c3a26 100644
--- a/src/tint/transform/add_empty_entry_point.h
+++ b/src/tint/transform/add_empty_entry_point.h
@@ -21,7 +21,8 @@
 namespace transform {
 
 /// Add an empty entry point to the module, if no other entry points exist.
-class AddEmptyEntryPoint : public Castable<AddEmptyEntryPoint, Transform> {
+class AddEmptyEntryPoint final
+    : public Castable<AddEmptyEntryPoint, Transform> {
  public:
   /// Constructor
   AddEmptyEntryPoint();
diff --git a/src/tint/transform/add_spirv_block_attribute.h b/src/tint/transform/add_spirv_block_attribute.h
index dc8258d..5e68dad 100644
--- a/src/tint/transform/add_spirv_block_attribute.h
+++ b/src/tint/transform/add_spirv_block_attribute.h
@@ -28,12 +28,12 @@
 /// store type of a buffer. If that structure is nested inside another structure
 /// or an array, then it is wrapped inside another structure which gets the
 /// `@internal(spirv_block)` attribute instead.
-class AddSpirvBlockAttribute
+class AddSpirvBlockAttribute final
     : public Castable<AddSpirvBlockAttribute, Transform> {
  public:
   /// SpirvBlockAttribute is an InternalAttribute that is used to decorate a
   // structure that needs a SPIR-V block attribute.
-  class SpirvBlockAttribute
+  class SpirvBlockAttribute final
       : public Castable<SpirvBlockAttribute, ast::InternalAttribute> {
    public:
     /// Constructor
diff --git a/src/tint/transform/array_length_from_uniform.h b/src/tint/transform/array_length_from_uniform.h
index 109db8e..2505f17 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
+class ArrayLengthFromUniform final
     : public Castable<ArrayLengthFromUniform, Transform> {
  public:
   /// Constructor
@@ -61,7 +61,7 @@
   ~ArrayLengthFromUniform() override;
 
   /// Configuration options for the ArrayLengthFromUniform transform.
-  struct Config : public Castable<Data, transform::Data> {
+  struct Config final : public Castable<Data, transform::Data> {
     /// Constructor
     /// @param ubo_bp the binding point to use for the generated uniform buffer.
     explicit Config(sem::BindingPoint ubo_bp);
@@ -86,7 +86,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 : public Castable<Result, transform::Data> {
+  struct Result final : public 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 5cfa3ec..f87f995 100644
--- a/src/tint/transform/binding_remapper.h
+++ b/src/tint/transform/binding_remapper.h
@@ -29,7 +29,7 @@
 
 /// BindingRemapper is a transform used to remap resource binding points and
 /// access controls.
-class BindingRemapper : public Castable<BindingRemapper, Transform> {
+class BindingRemapper final : public Castable<BindingRemapper, Transform> {
  public:
   /// BindingPoints is a map of old binding point to new binding point
   using BindingPoints = std::unordered_map<BindingPoint, BindingPoint>;
@@ -39,7 +39,7 @@
 
   /// Remappings is consumed by the BindingRemapper transform.
   /// Data holds information about shader usage and constant buffer offsets.
-  struct Remappings : public Castable<Data, transform::Data> {
+  struct Remappings final : public 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.h b/src/tint/transform/builtin_polyfill.h
index 131e08a..dec716e 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -21,7 +21,7 @@
 namespace transform {
 
 /// Implements builtins for backends that do not have a native implementation.
-class BuiltinPolyfill : public Castable<BuiltinPolyfill, Transform> {
+class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
  public:
   /// Constructor
   BuiltinPolyfill();
@@ -56,7 +56,7 @@
 
   /// Config is consumed by the BuiltinPolyfill transform.
   /// Config specifies the builtins that should be polyfilled.
-  struct Config : public Castable<Data, transform::Data> {
+  struct Config final : public 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 cc18faa..dfc1508 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 : public Castable<CalculateArrayLength, Transform> {
+class CalculateArrayLength final
+    : public 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
+  class BufferSizeIntrinsic final
       : public Castable<BufferSizeIntrinsic, ast::InternalAttribute> {
    public:
     /// Constructor
diff --git a/src/tint/transform/canonicalize_entry_point_io.h b/src/tint/transform/canonicalize_entry_point_io.h
index 1d65e41..48b7f40 100644
--- a/src/tint/transform/canonicalize_entry_point_io.h
+++ b/src/tint/transform/canonicalize_entry_point_io.h
@@ -83,7 +83,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * Unshadow
-class CanonicalizeEntryPointIO
+class CanonicalizeEntryPointIO final
     : public Castable<CanonicalizeEntryPointIO, Transform> {
  public:
   /// ShaderStyle is an enumerator of different ways to emit shader IO.
@@ -99,7 +99,7 @@
   };
 
   /// Configuration options for the transform.
-  struct Config : public Castable<Config, Data> {
+  struct Config final : public 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/combine_samplers.h b/src/tint/transform/combine_samplers.h
index e545786..f55c275 100644
--- a/src/tint/transform/combine_samplers.h
+++ b/src/tint/transform/combine_samplers.h
@@ -53,7 +53,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 : public Castable<CombineSamplers, Transform> {
+class CombineSamplers final : public Castable<CombineSamplers, Transform> {
  public:
   /// A pair of binding points.
   using SamplerTexturePair = sem::SamplerTexturePair;
@@ -63,7 +63,7 @@
 
   /// The client-provided mapping from separate texture and sampler binding
   /// points to combined sampler binding point.
-  struct BindingInfo : public Castable<Data, transform::Data> {
+  struct BindingInfo final : public 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.h b/src/tint/transform/decompose_memory_access.h
index 11b4450..d1c2ce1 100644
--- a/src/tint/transform/decompose_memory_access.h
+++ b/src/tint/transform/decompose_memory_access.h
@@ -30,14 +30,14 @@
 /// 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
+class DecomposeMemoryAccess final
     : public 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 : public Castable<Intrinsic, ast::InternalAttribute> {
+  class Intrinsic final : public 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 07d3353..ccf8691 100644
--- a/src/tint/transform/decompose_strided_array.h
+++ b/src/tint/transform/decompose_strided_array.h
@@ -28,7 +28,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
-class DecomposeStridedArray
+class DecomposeStridedArray final
     : public Castable<DecomposeStridedArray, Transform> {
  public:
   /// Constructor
diff --git a/src/tint/transform/decompose_strided_matrix.h b/src/tint/transform/decompose_strided_matrix.h
index eb908ff..f223178 100644
--- a/src/tint/transform/decompose_strided_matrix.h
+++ b/src/tint/transform/decompose_strided_matrix.h
@@ -28,7 +28,7 @@
 ///
 /// @note Depends on the following transforms to have been run first:
 /// * SimplifyPointers
-class DecomposeStridedMatrix
+class DecomposeStridedMatrix final
     : public Castable<DecomposeStridedMatrix, Transform> {
  public:
   /// Constructor
diff --git a/src/tint/transform/first_index_offset.h b/src/tint/transform/first_index_offset.h
index ce3135e..21636f3 100644
--- a/src/tint/transform/first_index_offset.h
+++ b/src/tint/transform/first_index_offset.h
@@ -58,12 +58,12 @@
 ///   }
 /// ```
 ///
-class FirstIndexOffset : public Castable<FirstIndexOffset, Transform> {
+class FirstIndexOffset final : public Castable<FirstIndexOffset, Transform> {
  public:
   /// BindingPoint is consumed by the FirstIndexOffset transform.
   /// BindingPoint specifies the binding point of the first index uniform
   /// buffer.
-  struct BindingPoint : public Castable<BindingPoint, transform::Data> {
+  struct BindingPoint final : public Castable<BindingPoint, transform::Data> {
     /// Constructor
     BindingPoint();
 
@@ -83,7 +83,7 @@
 
   /// Data is outputted by the FirstIndexOffset transform.
   /// Data holds information about shader usage and constant buffer offsets.
-  struct Data : public Castable<Data, transform::Data> {
+  struct Data final : public Castable<Data, transform::Data> {
     /// Constructor
     /// @param has_vtx_index True if the shader uses vertex_index
     /// @param has_inst_index True if the shader uses instance_index
diff --git a/src/tint/transform/fold_constants.h b/src/tint/transform/fold_constants.h
index 564ade0..7f964d1 100644
--- a/src/tint/transform/fold_constants.h
+++ b/src/tint/transform/fold_constants.h
@@ -21,7 +21,7 @@
 namespace transform {
 
 /// FoldConstants transforms the AST by folding constant expressions
-class FoldConstants : public Castable<FoldConstants, Transform> {
+class FoldConstants final : public Castable<FoldConstants, Transform> {
  public:
   /// Constructor
   FoldConstants();
diff --git a/src/tint/transform/fold_trivial_single_use_lets.h b/src/tint/transform/fold_trivial_single_use_lets.h
index 780fcee..858f6da 100644
--- a/src/tint/transform/fold_trivial_single_use_lets.h
+++ b/src/tint/transform/fold_trivial_single_use_lets.h
@@ -34,7 +34,7 @@
 ///   single usage.
 /// These rules prevent any hoisting of the let that may affect execution
 /// behaviour.
-class FoldTrivialSingleUseLets
+class FoldTrivialSingleUseLets final
     : public Castable<FoldTrivialSingleUseLets, Transform> {
  public:
   /// Constructor
diff --git a/src/tint/transform/for_loop_to_loop.h b/src/tint/transform/for_loop_to_loop.h
index c400448..94b0460 100644
--- a/src/tint/transform/for_loop_to_loop.h
+++ b/src/tint/transform/for_loop_to_loop.h
@@ -22,7 +22,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 : public Castable<ForLoopToLoop, Transform> {
+class ForLoopToLoop final : public Castable<ForLoopToLoop, Transform> {
  public:
   /// Constructor
   ForLoopToLoop();
diff --git a/src/tint/transform/glsl.h b/src/tint/transform/glsl.h
index b637056..baec29a 100644
--- a/src/tint/transform/glsl.h
+++ b/src/tint/transform/glsl.h
@@ -29,10 +29,10 @@
 /// Glsl is a transform used to sanitize a Program for use with the Glsl writer.
 /// Passing a non-sanitized Program to the Glsl writer will result in undefined
 /// behavior.
-class Glsl : public Castable<Glsl, Transform> {
+class Glsl final : public Castable<Glsl, Transform> {
  public:
   /// Configuration options for the Glsl sanitizer transform.
-  struct Config : public Castable<Data, transform::Data> {
+  struct Config final : public Castable<Data, transform::Data> {
     /// Constructor
     /// @param entry_point the root entry point function to generate
     /// @param disable_workgroup_init `true` to disable workgroup memory zero