Import Tint changes from Dawn

Changes:
  - dfb62150dfe7659f87286c53664e0cecbf196fb9 Remove program_builder include from type/ by dan sinclair <dsinclair@chromium.org>
  - 6fc130bfcc7d33c981c8a845f26f6e9a9700c7ff tint/reader/wgsl/lexer: Make '>>=' splittable by Ben Clayton <bclayton@google.com>
  - 5f3449f000efcc68f3e9cff878cbd4d2dabacdd5 tint/ast: Add DiagnosticControl AST node by James Price <jrprice@google.com>
  - 3272ac7e177fc658c875c835197afa845aa003f2 Pull unicode files out to own target. by dan sinclair <dsinclair@chromium.org>
  - 3085e23fc709e5f64fc0c658225c58276f2d8c12 Move SamplerKind out of ast and into type. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: dfb62150dfe7659f87286c53664e0cecbf196fb9
Change-Id: I2691b1fa8d418259d5d93d020a73c07df8ff00a0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/117527
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 4f6e892..9a14d8a 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -230,6 +230,8 @@
     "ast/depth_multisampled_texture.h",
     "ast/depth_texture.cc",
     "ast/depth_texture.h",
+    "ast/diagnostic_control.cc",
+    "ast/diagnostic_control.h",
     "ast/disable_validation_attribute.cc",
     "ast/disable_validation_attribute.h",
     "ast/discard_statement.cc",
@@ -448,8 +450,6 @@
     "symbol.h",
     "symbol_table.cc",
     "symbol_table.h",
-    "text/unicode.cc",
-    "text/unicode.h",
     "tint.cc",
     "traits.h",
     "type/abstract_float.h",
@@ -476,6 +476,7 @@
     "type/reference.h",
     "type/sampled_texture.h",
     "type/sampler.h",
+    "type/sampler_kind.h",
     "type/short_name.h",
     "type/storage_texture.h",
     "type/struct.h",
@@ -518,6 +519,15 @@
   } else {
     sources += [ "diagnostic/printer_other.cc" ]
   }
+
+  deps = [ ":libtint_text_src" ]
+}
+
+libtint_source_set("libtint_text_src") {
+  sources = [
+    "text/unicode.cc",
+    "text/unicode.h",
+  ]
 }
 
 libtint_source_set("libtint_transform_src") {
@@ -625,7 +635,10 @@
     "transform/zero_init_workgroup_memory.cc",
     "transform/zero_init_workgroup_memory.h",
   ]
-  deps = [ ":libtint_core_all_src" ]
+  deps = [
+    ":libtint_core_all_src",
+    ":libtint_text_src",
+  ]
 }
 
 libtint_source_set("libtint_sem_src") {
@@ -739,6 +752,8 @@
     "type/sampled_texture.h",
     "type/sampler.cc",
     "type/sampler.h",
+    "type/sampler_kind.cc",
+    "type/sampler_kind.h",
     "type/short_name.cc",
     "type/short_name.h",
     "type/storage_texture.cc",
@@ -788,6 +803,7 @@
     ":libtint_constant_src",
     ":libtint_core_all_src",
     ":libtint_sem_src",
+    ":libtint_text_src",
     ":libtint_transform_src",
     ":libtint_type_src",
     ":libtint_writer_src",
@@ -906,6 +922,7 @@
   deps = [
     ":libtint_core_all_src",
     ":libtint_reader_src",
+    ":libtint_text_src",
   ]
 }
 
@@ -1163,6 +1180,7 @@
       "ast/case_statement_test.cc",
       "ast/compound_assignment_statement_test.cc",
       "ast/continue_statement_test.cc",
+      "ast/diagnostic_control_test.cc",
       "ast/discard_statement_test.cc",
       "ast/enable_test.cc",
       "ast/extension_test.cc",
@@ -1353,6 +1371,7 @@
 
   tint_unittests_source_set("tint_unittests_text_src") {
     sources = [ "text/unicode_test.cc" ]
+    deps = [ ":libtint_text_src" ]
   }
 
   tint_unittests_source_set("tint_unittests_transform_src") {
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 95f2c86..438ab85 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -125,6 +125,8 @@
   ast/depth_multisampled_texture.h
   ast/depth_texture.cc
   ast/depth_texture.h
+  ast/diagnostic_control.cc
+  ast/diagnostic_control.h
   ast/disable_validation_attribute.cc
   ast/disable_validation_attribute.h
   ast/discard_statement.cc
@@ -508,6 +510,8 @@
   type/sampled_texture.h
   type/sampler.cc
   type/sampler.h
+  type/sampler_kind.cc
+  type/sampler_kind.h
   type/storage_texture.cc
   type/storage_texture.h
   type/struct.cc
@@ -567,6 +571,7 @@
 )
 
 tint_generated(ast/builtin_value BENCH TEST)
+tint_generated(ast/diagnostic_control BENCH TEST)
 tint_generated(ast/extension BENCH TEST)
 tint_generated(ast/interpolate_attribute BENCH TEST)
 tint_generated(resolver/init_conv_intrinsic)
@@ -827,6 +832,7 @@
     ast/continue_statement_test.cc
     ast/depth_multisampled_texture_test.cc
     ast/depth_texture_test.cc
+    ast/diagnostic_control_test.cc
     ast/discard_statement_test.cc
     ast/enable_test.cc
     ast/external_texture_test.cc
diff --git a/src/tint/ast/builtin_texture_helper_test.cc b/src/tint/ast/builtin_texture_helper_test.cc
index b8fefa6..d25ec0d 100644
--- a/src/tint/ast/builtin_texture_helper_test.cc
+++ b/src/tint/ast/builtin_texture_helper_test.cc
@@ -26,7 +26,7 @@
 TextureOverloadCase::TextureOverloadCase(ValidTextureOverload o,
                                          const char* desc,
                                          TextureKind tk,
-                                         ast::SamplerKind sk,
+                                         type::SamplerKind sk,
                                          type::TextureDimension dims,
                                          TextureDataType datatype,
                                          const char* f,
@@ -186,7 +186,7 @@
             ValidTextureOverload::kDimensions1d,
             "textureDimensions(t : texture_1d<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k1d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -196,7 +196,7 @@
             ValidTextureOverload::kDimensions2d,
             "textureDimensions(t : texture_2d<f32>) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -207,7 +207,7 @@
             "textureDimensions(t     : texture_2d<f32>,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -217,7 +217,7 @@
             ValidTextureOverload::kDimensions2dArray,
             "textureDimensions(t : texture_2d_array<f32>) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -228,7 +228,7 @@
             "textureDimensions(t     : texture_2d_array<f32>,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -238,7 +238,7 @@
             ValidTextureOverload::kDimensions3d,
             "textureDimensions(t : texture_3d<f32>) -> vec3<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -249,7 +249,7 @@
             "textureDimensions(t     : texture_3d<f32>,\n"
             "                  level : i32) -> vec3<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -259,7 +259,7 @@
             ValidTextureOverload::kDimensionsCube,
             "textureDimensions(t : texture_cube<f32>) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureDimensions",
@@ -270,7 +270,7 @@
             "textureDimensions(t     : texture_cube<f32>,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureDimensions",
@@ -280,7 +280,7 @@
             ValidTextureOverload::kDimensionsCubeArray,
             "textureDimensions(t : texture_cube_array<f32>) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -291,7 +291,7 @@
             "textureDimensions(t     : texture_cube_array<f32>,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -301,7 +301,7 @@
             ValidTextureOverload::kDimensionsMultisampled2d,
             "textureDimensions(t : texture_multisampled_2d<f32>)-> vec2<u32>",
             TextureKind::kMultisampled,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -311,7 +311,7 @@
             ValidTextureOverload::kDimensionsDepth2d,
             "textureDimensions(t : texture_depth_2d) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -322,7 +322,7 @@
             "textureDimensions(t     : texture_depth_2d,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -332,7 +332,7 @@
             ValidTextureOverload::kDimensionsDepth2dArray,
             "textureDimensions(t : texture_depth_2d_array) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -343,7 +343,7 @@
             "textureDimensions(t     : texture_depth_2d_array,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -353,7 +353,7 @@
             ValidTextureOverload::kDimensionsDepthCube,
             "textureDimensions(t : texture_depth_cube) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureDimensions",
@@ -364,7 +364,7 @@
             "textureDimensions(t     : texture_depth_cube,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureDimensions",
@@ -374,7 +374,7 @@
             ValidTextureOverload::kDimensionsDepthCubeArray,
             "textureDimensions(t : texture_depth_cube_array) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -385,7 +385,7 @@
             "textureDimensions(t     : texture_depth_cube_array,\n"
             "                  level : i32) -> vec2<u32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureDimensions",
@@ -395,7 +395,7 @@
             ValidTextureOverload::kDimensionsDepthMultisampled2d,
             "textureDimensions(t : texture_depth_multisampled_2d) -> vec2<u32>",
             TextureKind::kDepthMultisampled,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureDimensions",
@@ -449,7 +449,7 @@
             "              s         : sampler,\n"
             "              coords    : vec2<f32>) -> vec4<T>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureGather",
@@ -468,7 +468,7 @@
             "              coords    : vec2<f32>,\n"
             "              offset    : vec2<i32>) -> vec4<T>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureGather",
@@ -488,7 +488,7 @@
             "              coords      : vec2<f32>,\n"
             "              array_index : i32) -> vec4<T>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureGather",
@@ -509,7 +509,7 @@
             "              array_index : u32,\n"
             "              offset      : vec2<i32>) -> vec4<T>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureGather",
@@ -529,7 +529,7 @@
             "              s         : sampler,\n"
             "              coords    : vec3<f32>) -> vec4<T>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureGather",
@@ -548,7 +548,7 @@
             "              coords      : vec3<f32>,\n"
             "              array_index : u32) -> vec4<T>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureGather",
@@ -566,7 +566,7 @@
             "              s      : sampler,\n"
             "              coords : vec2<f32>) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureGather",
@@ -583,7 +583,7 @@
             "              coords : vec2<f32>,\n"
             "              offset : vec2<i32>) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureGather",
@@ -601,7 +601,7 @@
             "              coords      : vec2<f32>,\n"
             "              array_index : u32) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureGather",
@@ -620,7 +620,7 @@
             "              array_index : i32,\n"
             "              offset      : vec2<i32>) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureGather",
@@ -638,7 +638,7 @@
             "              s      : sampler,\n"
             "              coords : vec3<f32>) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureGather",
@@ -655,7 +655,7 @@
             "              coords      : vec3<f32>,\n"
             "              array_index : u32) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureGather",
@@ -673,7 +673,7 @@
             "                     coords    : vec2<f32>,\n"
             "                     depth_ref : f32) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureGatherCompare",
@@ -692,7 +692,7 @@
             "                     depth_ref : f32,\n"
             "                     offset    : vec2<i32>) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureGatherCompare",
@@ -712,7 +712,7 @@
             "                     array_index : i32,\n"
             "                     depth_ref   : f32) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureGatherCompare",
@@ -733,7 +733,7 @@
             "                     depth_ref   : f32,\n"
             "                     offset      : vec2<i32>) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureGatherCompare",
@@ -753,7 +753,7 @@
             "                     coords    : vec3<f32>,\n"
             "                     depth_ref : f32) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureGatherCompare",
@@ -772,7 +772,7 @@
             "                     array_index : u32,\n"
             "                     depth_ref   : f32) -> vec4<f32>",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureGatherCompare",
@@ -788,7 +788,7 @@
             ValidTextureOverload::kNumLayers2dArray,
             "textureNumLayers(t : texture_2d_array<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureNumLayers",
@@ -798,7 +798,7 @@
             ValidTextureOverload::kNumLayersCubeArray,
             "textureNumLayers(t : texture_cube_array<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureNumLayers",
@@ -808,7 +808,7 @@
             ValidTextureOverload::kNumLayersDepth2dArray,
             "textureNumLayers(t : texture_depth_2d_array) -> u32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureNumLayers",
@@ -818,7 +818,7 @@
             ValidTextureOverload::kNumLayersDepthCubeArray,
             "textureNumLayers(t : texture_depth_cube_array) -> u32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureNumLayers",
@@ -838,7 +838,7 @@
             ValidTextureOverload::kNumLevels2d,
             "textureNumLevels(t : texture_2d<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -848,7 +848,7 @@
             ValidTextureOverload::kNumLevels2dArray,
             "textureNumLevels(t : texture_2d_array<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -858,7 +858,7 @@
             ValidTextureOverload::kNumLevels3d,
             "textureNumLevels(t : texture_3d<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -868,7 +868,7 @@
             ValidTextureOverload::kNumLevelsCube,
             "textureNumLevels(t : texture_cube<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -878,7 +878,7 @@
             ValidTextureOverload::kNumLevelsCubeArray,
             "textureNumLevels(t : texture_cube_array<f32>) -> u32",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -888,7 +888,7 @@
             ValidTextureOverload::kNumLevelsDepth2d,
             "textureNumLevels(t : texture_depth_2d) -> u32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -898,7 +898,7 @@
             ValidTextureOverload::kNumLevelsDepth2dArray,
             "textureNumLevels(t : texture_depth_2d_array) -> u32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -908,7 +908,7 @@
             ValidTextureOverload::kNumLevelsDepthCube,
             "textureNumLevels(t : texture_depth_cube) -> u32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -918,7 +918,7 @@
             ValidTextureOverload::kNumLevelsDepthCubeArray,
             "textureNumLevels(t : texture_depth_cube_array) -> u32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureNumLevels",
@@ -928,7 +928,7 @@
             ValidTextureOverload::kNumSamplesMultisampled2d,
             "textureNumSamples(t : texture_multisampled_2d<f32>) -> u32",
             TextureKind::kMultisampled,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureNumSamples",
@@ -938,7 +938,7 @@
             ValidTextureOverload::kNumSamplesDepthMultisampled2d,
             "textureNumSamples(t : texture_depth_multisampled_2d<f32>) -> u32",
             TextureKind::kMultisampled,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureNumSamples",
@@ -950,7 +950,7 @@
             "              s      : sampler,\n"
             "              coords : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k1d,
             TextureDataType::kF32,
             "textureSample",
@@ -966,7 +966,7 @@
             "              s      : sampler,\n"
             "              coords : vec2<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSample",
@@ -983,7 +983,7 @@
             "              coords : vec2<f32>\n"
             "              offset : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSample",
@@ -1001,7 +1001,7 @@
             "              coords      : vec2<f32>,\n"
             "              array_index : i32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSample",
@@ -1020,7 +1020,7 @@
             "              array_index : u32\n"
             "              offset      : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSample",
@@ -1038,7 +1038,7 @@
             "              s      : sampler,\n"
             "              coords : vec3<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSample",
@@ -1055,7 +1055,7 @@
             "              coords : vec3<f32>\n"
             "              offset : vec3<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSample",
@@ -1072,7 +1072,7 @@
             "              s      : sampler,\n"
             "              coords : vec3<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSample",
@@ -1089,7 +1089,7 @@
             "              coords      : vec3<f32>,\n"
             "              array_index : i32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSample",
@@ -1106,7 +1106,7 @@
             "              s      : sampler,\n"
             "              coords : vec2<f32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSample",
@@ -1123,7 +1123,7 @@
             "              coords : vec2<f32>\n"
             "              offset : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSample",
@@ -1141,7 +1141,7 @@
             "              coords      : vec2<f32>,\n"
             "              array_index : i32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSample",
@@ -1160,7 +1160,7 @@
             "              array_index : i32\n"
             "              offset      : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSample",
@@ -1178,7 +1178,7 @@
             "              s      : sampler,\n"
             "              coords : vec3<f32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSample",
@@ -1195,7 +1195,7 @@
             "              coords      : vec3<f32>,\n"
             "              array_index : u32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSample",
@@ -1213,7 +1213,7 @@
             "                  coords : vec2<f32>,\n"
             "                  bias   : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1232,7 +1232,7 @@
             "                  bias   : f32,\n"
             "                  offset : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1252,7 +1252,7 @@
             "                  array_index : u32,\n"
             "                  bias        : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1273,7 +1273,7 @@
             "                  bias        : f32,\n"
             "                  offset      : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1293,7 +1293,7 @@
             "                  coords : vec3<f32>,\n"
             "                  bias   : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1312,7 +1312,7 @@
             "                  bias   : f32,\n"
             "                  offset : vec3<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1331,7 +1331,7 @@
             "                  coords : vec3<f32>,\n"
             "                  bias   : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1350,7 +1350,7 @@
             "                  array_index : i32,\n"
             "                  bias        : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSampleBias",
@@ -1369,7 +1369,7 @@
             "                   coords : vec2<f32>,\n"
             "                   level  : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1388,7 +1388,7 @@
             "                   level  : f32,\n"
             "                   offset : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1408,7 +1408,7 @@
             "                   array_index : i32,\n"
             "                   level       : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1429,7 +1429,7 @@
             "                   level       : f32,\n"
             "                   offset      : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1449,7 +1449,7 @@
             "                   coords : vec3<f32>,\n"
             "                   level  : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1468,7 +1468,7 @@
             "                   level  : f32,\n"
             "                   offset : vec3<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1487,7 +1487,7 @@
             "                   coords : vec3<f32>,\n"
             "                   level  : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1506,7 +1506,7 @@
             "                   array_index : i32,\n"
             "                   level       : f32) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1525,7 +1525,7 @@
             "                   coords : vec2<f32>,\n"
             "                   level  : u32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1544,7 +1544,7 @@
             "                   level  : i32,\n"
             "                   offset : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1564,7 +1564,7 @@
             "                   array_index : u32,\n"
             "                   level       : u32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1585,7 +1585,7 @@
             "                   level       : u32,\n"
             "                   offset      : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1605,7 +1605,7 @@
             "                   coords : vec3<f32>,\n"
             "                   level  : i32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1624,7 +1624,7 @@
             "                   array_index : i32,\n"
             "                   level       : i32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSampleLevel",
@@ -1644,7 +1644,7 @@
             "                  ddx    : vec2<f32>,\n"
             "                  ddy    : vec2<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1665,7 +1665,7 @@
             "                  ddy    : vec2<f32>,\n"
             "                  offset : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1687,7 +1687,7 @@
             "                  ddx         : vec2<f32>,\n"
             "                  ddy         : vec2<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1710,7 +1710,7 @@
             "                  ddy         : vec2<f32>,\n"
             "                  offset      : vec2<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1732,7 +1732,7 @@
             "                  ddx    : vec3<f32>,\n"
             "                  ddy    : vec3<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1753,7 +1753,7 @@
             "                  ddy    : vec3<f32>,\n"
             "                  offset : vec3<i32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::k3d,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1774,7 +1774,7 @@
             "                  ddx    : vec3<f32>,\n"
             "                  ddy    : vec3<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1795,7 +1795,7 @@
             "                  ddx         : vec3<f32>,\n"
             "                  ddy         : vec3<f32>) -> vec4<f32>",
             TextureKind::kRegular,
-            ast::SamplerKind::kSampler,
+            type::SamplerKind::kSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSampleGrad",
@@ -1815,7 +1815,7 @@
             "                     coords    : vec2<f32>,\n"
             "                     depth_ref : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleCompare",
@@ -1834,7 +1834,7 @@
             "                     depth_ref : f32,\n"
             "                     offset    : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleCompare",
@@ -1854,7 +1854,7 @@
             "                     array_index : i32,\n"
             "                     depth_ref   : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleCompare",
@@ -1875,7 +1875,7 @@
             "                     depth_ref   : f32,\n"
             "                     offset      : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleCompare",
@@ -1895,7 +1895,7 @@
             "                     coords    : vec3<f32>,\n"
             "                     depth_ref : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSampleCompare",
@@ -1914,7 +1914,7 @@
             "                     array_index : i32,\n"
             "                     depth_ref   : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSampleCompare",
@@ -1933,7 +1933,7 @@
             "                          coords    : vec2<f32>,\n"
             "                          depth_ref : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleCompareLevel",
@@ -1952,7 +1952,7 @@
             "                          depth_ref : f32,\n"
             "                          offset    : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2d,
             TextureDataType::kF32,
             "textureSampleCompareLevel",
@@ -1972,7 +1972,7 @@
             "                          array_index : i32,\n"
             "                          depth_ref   : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleCompareLevel",
@@ -1993,7 +1993,7 @@
             "                          depth_ref   : f32,\n"
             "                          offset      : vec2<i32>) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::k2dArray,
             TextureDataType::kF32,
             "textureSampleCompareLevel",
@@ -2013,7 +2013,7 @@
             "                          coords      : vec3<f32>,\n"
             "                          depth_ref   : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::kCube,
             TextureDataType::kF32,
             "textureSampleCompareLevel",
@@ -2032,7 +2032,7 @@
             "                          array_index : i32,\n"
             "                          depth_ref   : f32) -> f32",
             TextureKind::kDepth,
-            ast::SamplerKind::kComparisonSampler,
+            type::SamplerKind::kComparisonSampler,
             type::TextureDimension::kCubeArray,
             TextureDataType::kF32,
             "textureSampleCompareLevel",
diff --git a/src/tint/ast/builtin_texture_helper_test.h b/src/tint/ast/builtin_texture_helper_test.h
index a17dd37..7391e8c 100644
--- a/src/tint/ast/builtin_texture_helper_test.h
+++ b/src/tint/ast/builtin_texture_helper_test.h
@@ -185,7 +185,7 @@
     TextureOverloadCase(ValidTextureOverload,
                         const char*,
                         TextureKind,
-                        ast::SamplerKind,
+                        type::SamplerKind,
                         type::TextureDimension,
                         TextureDataType,
                         const char*,
@@ -236,7 +236,7 @@
     const TextureKind texture_kind;
     /// The sampler kind for the sampler parameter
     /// Used only when texture_kind is not kStorage
-    ast::SamplerKind const sampler_kind = ast::SamplerKind::kSampler;
+    type::SamplerKind const sampler_kind = type::SamplerKind::kSampler;
     /// The access control for the storage texture
     /// Used only when texture_kind is kStorage
     type::Access const access = type::Access::kReadWrite;
diff --git a/src/tint/ast/diagnostic_control.cc b/src/tint/ast/diagnostic_control.cc
new file mode 100644
index 0000000..d83e9c2
--- /dev/null
+++ b/src/tint/ast/diagnostic_control.cc
@@ -0,0 +1,76 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/ast/diagnostic_control.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/ast/diagnostic_control.h"
+
+#include <string>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::DiagnosticControl);
+
+namespace tint::ast {
+
+DiagnosticControl::~DiagnosticControl() = default;
+
+const DiagnosticControl* DiagnosticControl::Clone(CloneContext* ctx) const {
+    auto src = ctx->Clone(source);
+    auto rule = ctx->Clone(rule_name);
+    return ctx->dst->create<DiagnosticControl>(src, severity, rule);
+}
+
+/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
+DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str) {
+    if (str == "error") {
+        return DiagnosticSeverity::kError;
+    }
+    if (str == "info") {
+        return DiagnosticSeverity::kInfo;
+    }
+    if (str == "off") {
+        return DiagnosticSeverity::kOff;
+    }
+    if (str == "warning") {
+        return DiagnosticSeverity::kWarning;
+    }
+    return DiagnosticSeverity::kUndefined;
+}
+
+std::ostream& operator<<(std::ostream& out, DiagnosticSeverity value) {
+    switch (value) {
+        case DiagnosticSeverity::kUndefined:
+            return out << "undefined";
+        case DiagnosticSeverity::kError:
+            return out << "error";
+        case DiagnosticSeverity::kInfo:
+            return out << "info";
+        case DiagnosticSeverity::kOff:
+            return out << "off";
+        case DiagnosticSeverity::kWarning:
+            return out << "warning";
+    }
+    return out << "<unknown>";
+}
+
+}  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control.cc.tmpl b/src/tint/ast/diagnostic_control.cc.tmpl
new file mode 100644
index 0000000..c5d1a73
--- /dev/null
+++ b/src/tint/ast/diagnostic_control.cc.tmpl
@@ -0,0 +1,35 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include "src/tint/ast/diagnostic_control.h"
+
+#include <string>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::DiagnosticControl);
+
+namespace tint::ast {
+
+DiagnosticControl::~DiagnosticControl() = default;
+
+const DiagnosticControl* DiagnosticControl::Clone(CloneContext* ctx) const {
+    auto src = ctx->Clone(source);
+    auto rule = ctx->Clone(rule_name);
+    return ctx->dst->create<DiagnosticControl>(src, severity, rule);
+}
+
+{{ Eval "ParseEnum" (Sem.Enum "diagnostic_severity")}}
+
+{{ Eval "EnumOStream" (Sem.Enum "diagnostic_severity")}}
+
+}  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control.h b/src/tint/ast/diagnostic_control.h
new file mode 100644
index 0000000..f127c1f
--- /dev/null
+++ b/src/tint/ast/diagnostic_control.h
@@ -0,0 +1,96 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/ast/diagnostic_control.h.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
+#define SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
+
+#include <ostream>
+#include <string>
+
+#include "src/tint/ast/node.h"
+
+// Forward declarations
+namespace tint::ast {
+class IdentifierExpression;
+}  // namespace tint::ast
+
+namespace tint::ast {
+
+/// The diagnostic severity control.
+enum class DiagnosticSeverity {
+    kUndefined,
+    kError,
+    kInfo,
+    kOff,
+    kWarning,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the DiagnosticSeverity
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, DiagnosticSeverity value);
+
+/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
+DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str);
+
+constexpr const char* kDiagnosticSeverityStrings[] = {
+    "error",
+    "info",
+    "off",
+    "warning",
+};
+
+/// A diagnostic control used for diagnostic directives and attributes.
+class DiagnosticControl : public Castable<DiagnosticControl, Node> {
+  public:
+    /// Constructor
+    /// @param pid the identifier of the program that owns this node
+    /// @param nid the unique node identifier
+    /// @param src the source of this node
+    /// @param sev the diagnostic severity
+    /// @param rule the diagnostic rule name
+    DiagnosticControl(ProgramID pid,
+                      NodeID nid,
+                      const Source& src,
+                      DiagnosticSeverity sev,
+                      const IdentifierExpression* rule)
+        : Base(pid, nid, src), severity(sev), rule_name(rule) {}
+
+    ~DiagnosticControl() override;
+
+    /// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
+    /// @param ctx the clone context
+    /// @return the newly cloned node
+    const DiagnosticControl* Clone(CloneContext* ctx) const override;
+
+    /// The diagnostic severity control.
+    DiagnosticSeverity severity;
+
+    /// The diagnostic rule name.
+    const IdentifierExpression* rule_name;
+};
+
+}  // namespace tint::ast
+
+#endif  // SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
diff --git a/src/tint/ast/diagnostic_control.h.tmpl b/src/tint/ast/diagnostic_control.h.tmpl
new file mode 100644
index 0000000..5ddbc6d
--- /dev/null
+++ b/src/tint/ast/diagnostic_control.h.tmpl
@@ -0,0 +1,63 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control.h
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#ifndef SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
+#define SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
+
+#include <ostream>
+#include <string>
+
+#include "src/tint/ast/node.h"
+
+// Forward declarations
+namespace tint::ast {
+class IdentifierExpression;
+}  // namespace tint::ast
+
+namespace tint::ast {
+
+/// The diagnostic severity control.
+{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_severity") }}
+
+/// A diagnostic control used for diagnostic directives and attributes.
+class DiagnosticControl : public Castable<DiagnosticControl, Node> {
+  public:
+    /// Constructor
+    /// @param pid the identifier of the program that owns this node
+    /// @param nid the unique node identifier
+    /// @param src the source of this node
+    /// @param sev the diagnostic severity
+    /// @param rule the diagnostic rule name
+    DiagnosticControl(ProgramID pid,
+                      NodeID nid,
+                      const Source& src,
+                      DiagnosticSeverity sev,
+                      const IdentifierExpression* rule)
+        : Base(pid, nid, src), severity(sev), rule_name(rule) {}
+
+    ~DiagnosticControl() override;
+
+    /// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
+    /// @param ctx the clone context
+    /// @return the newly cloned node
+    const DiagnosticControl* Clone(CloneContext* ctx) const override;
+
+    /// The diagnostic severity control.
+    DiagnosticSeverity severity;
+
+    /// The diagnostic rule name.
+    const IdentifierExpression* rule_name;
+};
+
+}  // namespace tint::ast
+
+#endif  // SRC_TINT_AST_DIAGNOSTIC_CONTROL_H_
diff --git a/src/tint/ast/diagnostic_control_bench.cc b/src/tint/ast/diagnostic_control_bench.cc
new file mode 100644
index 0000000..d96f93b
--- /dev/null
+++ b/src/tint/ast/diagnostic_control_bench.cc
@@ -0,0 +1,50 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/ast/diagnostic_control_bench.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/ast/diagnostic_control.h"
+
+#include <array>
+
+#include "benchmark/benchmark.h"
+
+namespace tint::ast {
+namespace {
+
+void DiagnosticSeverityParser(::benchmark::State& state) {
+    std::array kStrings{
+        "erccr",    "3o",        "eVror",   "error",   "erro1",  "qqrJr",  "errll7r",
+        "ppqnfH",   "c",         "iGf",     "info",    "invii",  "inWWo",  "Mxxo",
+        "ogg",      "X",         "3ff",     "off",     "oEf",    "oPTT",   "dxxf",
+        "w44rning", "waSSniVVg", "RarR22g", "warning", "wFni9g", "waring", "VOORRHng",
+    };
+    for (auto _ : state) {
+        for (auto& str : kStrings) {
+            auto result = ParseDiagnosticSeverity(str);
+            benchmark::DoNotOptimize(result);
+        }
+    }
+}
+
+BENCHMARK(DiagnosticSeverityParser);
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_bench.cc.tmpl b/src/tint/ast/diagnostic_control_bench.cc.tmpl
new file mode 100644
index 0000000..48058ca
--- /dev/null
+++ b/src/tint/ast/diagnostic_control_bench.cc.tmpl
@@ -0,0 +1,25 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control_bench.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include "src/tint/ast/diagnostic_control.h"
+
+#include <array>
+
+#include "benchmark/benchmark.h"
+
+namespace tint::ast {
+namespace {
+
+{{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_severity")}}
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_test.cc b/src/tint/ast/diagnostic_control_test.cc
new file mode 100644
index 0000000..2dbd139
--- /dev/null
+++ b/src/tint/ast/diagnostic_control_test.cc
@@ -0,0 +1,104 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+//   src/tint/ast/diagnostic_control_test.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include <string>
+
+#include "src/tint/ast/diagnostic_control.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint::ast {
+namespace {
+
+using DiagnosticControlTest = TestHelper;
+
+TEST_F(DiagnosticControlTest, Creation) {
+    auto* name = Expr("foo");
+    Source source;
+    source.range.begin = Source::Location{20, 2};
+    source.range.end = Source::Location{20, 5};
+    auto* control = create<ast::DiagnosticControl>(source, DiagnosticSeverity::kWarning, name);
+    EXPECT_EQ(control->source.range.begin.line, 20u);
+    EXPECT_EQ(control->source.range.begin.column, 2u);
+    EXPECT_EQ(control->source.range.end.line, 20u);
+    EXPECT_EQ(control->source.range.end.column, 5u);
+    EXPECT_EQ(control->severity, DiagnosticSeverity::kWarning);
+    EXPECT_EQ(control->rule_name, name);
+}
+
+namespace diagnostic_severity_tests {
+
+namespace parse_print_tests {
+
+struct Case {
+    const char* string;
+    DiagnosticSeverity value;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+    return out << "'" << std::string(c.string) << "'";
+}
+
+static constexpr Case kValidCases[] = {
+    {"error", DiagnosticSeverity::kError},
+    {"info", DiagnosticSeverity::kInfo},
+    {"off", DiagnosticSeverity::kOff},
+    {"warning", DiagnosticSeverity::kWarning},
+};
+
+static constexpr Case kInvalidCases[] = {
+    {"erccr", DiagnosticSeverity::kUndefined},    {"3o", DiagnosticSeverity::kUndefined},
+    {"eVror", DiagnosticSeverity::kUndefined},    {"1nfo", DiagnosticSeverity::kUndefined},
+    {"iqfJ", DiagnosticSeverity::kUndefined},     {"illf77", DiagnosticSeverity::kUndefined},
+    {"oppqH", DiagnosticSeverity::kUndefined},    {"", DiagnosticSeverity::kUndefined},
+    {"Gb", DiagnosticSeverity::kUndefined},       {"warniivg", DiagnosticSeverity::kUndefined},
+    {"8WWrning", DiagnosticSeverity::kUndefined}, {"wxxning", DiagnosticSeverity::kUndefined},
+};
+
+using DiagnosticSeverityParseTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticSeverityParseTest, Parse) {
+    const char* string = GetParam().string;
+    DiagnosticSeverity expect = GetParam().value;
+    EXPECT_EQ(expect, ParseDiagnosticSeverity(string));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticSeverityParseTest, testing::ValuesIn(kValidCases));
+INSTANTIATE_TEST_SUITE_P(InvalidCases,
+                         DiagnosticSeverityParseTest,
+                         testing::ValuesIn(kInvalidCases));
+
+using DiagnosticSeverityPrintTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticSeverityPrintTest, Print) {
+    DiagnosticSeverity value = GetParam().value;
+    const char* expect = GetParam().string;
+    EXPECT_EQ(expect, utils::ToString(value));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticSeverityPrintTest, testing::ValuesIn(kValidCases));
+
+}  // namespace parse_print_tests
+
+}  // namespace diagnostic_severity_tests
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_test.cc.tmpl b/src/tint/ast/diagnostic_control_test.cc.tmpl
new file mode 100644
index 0000000..a078cce
--- /dev/null
+++ b/src/tint/ast/diagnostic_control_test.cc.tmpl
@@ -0,0 +1,45 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate diagnostic_control_test.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include <string>
+
+#include "src/tint/ast/diagnostic_control.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint::ast {
+namespace {
+
+using DiagnosticControlTest = TestHelper;
+
+TEST_F(DiagnosticControlTest, Creation) {
+    auto* name = Expr("foo");
+    Source source;
+    source.range.begin = Source::Location{20, 2};
+    source.range.end = Source::Location{20, 5};
+    auto* control = create<ast::DiagnosticControl>(source,
+                                                   DiagnosticSeverity::kWarning, name);
+    EXPECT_EQ(control->source.range.begin.line, 20u);
+    EXPECT_EQ(control->source.range.begin.column, 2u);
+    EXPECT_EQ(control->source.range.end.line, 20u);
+    EXPECT_EQ(control->source.range.end.column, 5u);
+    EXPECT_EQ(control->severity, DiagnosticSeverity::kWarning);
+    EXPECT_EQ(control->rule_name, name);
+}
+
+namespace diagnostic_severity_tests {
+
+{{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_severity")}}
+
+}  // namespace diagnostic_severity_tests
+
+}  // namespace
+}  // namespace tint::ast
diff --git a/src/tint/ast/sampler.cc b/src/tint/ast/sampler.cc
index 5237380..28bba36 100644
--- a/src/tint/ast/sampler.cc
+++ b/src/tint/ast/sampler.cc
@@ -20,19 +20,7 @@
 
 namespace tint::ast {
 
-std::ostream& operator<<(std::ostream& out, SamplerKind kind) {
-    switch (kind) {
-        case SamplerKind::kSampler:
-            out << "sampler";
-            break;
-        case SamplerKind::kComparisonSampler:
-            out << "comparison_sampler";
-            break;
-    }
-    return out;
-}
-
-Sampler::Sampler(ProgramID pid, NodeID nid, const Source& src, SamplerKind k)
+Sampler::Sampler(ProgramID pid, NodeID nid, const Source& src, type::SamplerKind k)
     : Base(pid, nid, src), kind(k) {}
 
 Sampler::Sampler(Sampler&&) = default;
@@ -40,7 +28,7 @@
 Sampler::~Sampler() = default;
 
 std::string Sampler::FriendlyName(const SymbolTable&) const {
-    return kind == SamplerKind::kSampler ? "sampler" : "sampler_comparison";
+    return kind == type::SamplerKind::kSampler ? "sampler" : "sampler_comparison";
 }
 
 const Sampler* Sampler::Clone(CloneContext* ctx) const {
diff --git a/src/tint/ast/sampler.h b/src/tint/ast/sampler.h
index bcdf751..4227612 100644
--- a/src/tint/ast/sampler.h
+++ b/src/tint/ast/sampler.h
@@ -18,22 +18,10 @@
 #include <string>
 
 #include "src/tint/ast/type.h"
+#include "src/tint/type/sampler_kind.h"
 
 namespace tint::ast {
 
-/// The different kinds of samplers
-enum class SamplerKind {
-    /// A regular sampler
-    kSampler,
-    /// A comparison sampler
-    kComparisonSampler
-};
-
-/// @param out the std::ostream to write to
-/// @param kind the SamplerKind
-/// @return the std::ostream so calls can be chained
-std::ostream& operator<<(std::ostream& out, SamplerKind kind);
-
 /// A sampler type.
 class Sampler final : public Castable<Sampler, Type> {
   public:
@@ -42,13 +30,13 @@
     /// @param nid the unique node identifier
     /// @param src the source of this node
     /// @param kind the kind of sampler
-    Sampler(ProgramID pid, NodeID nid, const Source& src, SamplerKind kind);
+    Sampler(ProgramID pid, NodeID nid, const Source& src, type::SamplerKind kind);
     /// Move constructor
     Sampler(Sampler&&);
     ~Sampler() override;
 
     /// @returns true if this is a comparison sampler
-    bool IsComparison() const { return kind == SamplerKind::kComparisonSampler; }
+    bool IsComparison() const { return kind == type::SamplerKind::kComparisonSampler; }
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
@@ -61,7 +49,7 @@
     const Sampler* Clone(CloneContext* ctx) const override;
 
     /// The sampler type
-    const SamplerKind kind;
+    const type::SamplerKind kind;
 };
 
 }  // namespace tint::ast
diff --git a/src/tint/ast/sampler_test.cc b/src/tint/ast/sampler_test.cc
index 14500d9..a9d5103 100644
--- a/src/tint/ast/sampler_test.cc
+++ b/src/tint/ast/sampler_test.cc
@@ -22,23 +22,23 @@
 using AstSamplerTest = TestHelper;
 
 TEST_F(AstSamplerTest, Creation) {
-    auto* s = create<Sampler>(SamplerKind::kSampler);
-    EXPECT_EQ(s->kind, SamplerKind::kSampler);
+    auto* s = create<Sampler>(type::SamplerKind::kSampler);
+    EXPECT_EQ(s->kind, type::SamplerKind::kSampler);
 }
 
 TEST_F(AstSamplerTest, Creation_ComparisonSampler) {
-    auto* s = create<Sampler>(SamplerKind::kComparisonSampler);
-    EXPECT_EQ(s->kind, SamplerKind::kComparisonSampler);
+    auto* s = create<Sampler>(type::SamplerKind::kComparisonSampler);
+    EXPECT_EQ(s->kind, type::SamplerKind::kComparisonSampler);
     EXPECT_TRUE(s->IsComparison());
 }
 
 TEST_F(AstSamplerTest, FriendlyNameSampler) {
-    auto* s = create<Sampler>(SamplerKind::kSampler);
+    auto* s = create<Sampler>(type::SamplerKind::kSampler);
     EXPECT_EQ(s->FriendlyName(Symbols()), "sampler");
 }
 
 TEST_F(AstSamplerTest, FriendlyNameComparisonSampler) {
-    auto* s = create<Sampler>(SamplerKind::kComparisonSampler);
+    auto* s = create<Sampler>(type::SamplerKind::kComparisonSampler);
     EXPECT_EQ(s->FriendlyName(Symbols()), "sampler_comparison");
 }
 
diff --git a/src/tint/inspector/test_inspector_builder.h b/src/tint/inspector/test_inspector_builder.h
index cfba3ac..03d2727 100644
--- a/src/tint/inspector/test_inspector_builder.h
+++ b/src/tint/inspector/test_inspector_builder.h
@@ -333,11 +333,11 @@
     Inspector& Build();
 
     /// @returns the type for a SamplerKind::kSampler
-    const ast::Sampler* sampler_type() { return ty.sampler(ast::SamplerKind::kSampler); }
+    const ast::Sampler* sampler_type() { return ty.sampler(type::SamplerKind::kSampler); }
 
     /// @returns the type for a SamplerKind::kComparison
     const ast::Sampler* comparison_sampler_type() {
-        return ty.sampler(ast::SamplerKind::kComparisonSampler);
+        return ty.sampler(type::SamplerKind::kComparisonSampler);
     }
 
   protected:
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 08dc366..05fe8c9 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -40,6 +40,14 @@
   @internal point_size
 }
 
+// https://gpuweb.github.io/gpuweb/wgsl/#syntax-severity_control_name
+enum diagnostic_severity {
+  error
+  warning
+  info
+  off
+}
+
 // https://gpuweb.github.io/gpuweb/wgsl/#extension
 enum extension {
   // WGSL Extension "f16"
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 5e8f669..4e760de 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -40,6 +40,7 @@
 #include "src/tint/ast/continue_statement.h"
 #include "src/tint/ast/depth_multisampled_texture.h"
 #include "src/tint/ast/depth_texture.h"
+#include "src/tint/ast/diagnostic_control.h"
 #include "src/tint/ast/disable_validation_attribute.h"
 #include "src/tint/ast/discard_statement.h"
 #include "src/tint/ast/enable.h"
@@ -1000,14 +1001,14 @@
 
         /// @param kind the kind of sampler
         /// @returns the sampler
-        const ast::Sampler* sampler(ast::SamplerKind kind) const {
+        const ast::Sampler* sampler(type::SamplerKind kind) const {
             return builder->create<ast::Sampler>(kind);
         }
 
         /// @param source the Source of the node
         /// @param kind the kind of sampler
         /// @returns the sampler
-        const ast::Sampler* sampler(const Source& source, ast::SamplerKind kind) const {
+        const ast::Sampler* sampler(const Source& source, type::SamplerKind kind) const {
             return builder->create<ast::Sampler>(source, kind);
         }
 
@@ -3220,6 +3221,26 @@
                                                                   validation);
     }
 
+    /// Creates an ast::DiagnosticControl
+    /// @param source the source information
+    /// @param severity the diagnostic severity control
+    /// @param rule_name the diagnostic rule name
+    /// @returns the diagnostic control pointer
+    const ast::DiagnosticControl* DiagnosticControl(const Source& source,
+                                                    ast::DiagnosticSeverity severity,
+                                                    const ast::IdentifierExpression* rule_name) {
+        return create<ast::DiagnosticControl>(source, severity, rule_name);
+    }
+
+    /// Creates an ast::DiagnosticControl
+    /// @param severity the diagnostic severity control
+    /// @param rule_name the diagnostic rule name
+    /// @returns the diagnostic control pointer
+    const ast::DiagnosticControl* DiagnosticControl(ast::DiagnosticSeverity severity,
+                                                    const ast::IdentifierExpression* rule_name) {
+        return create<ast::DiagnosticControl>(source_, severity, rule_name);
+    }
+
     /// Sets the current builder source to `src`
     /// @param src the Source used for future create() calls
     void SetSource(const Source& src) {
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 8d749a2..59b64b1 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -2456,8 +2456,8 @@
     const Type* ast_handle_type = nullptr;
     if (usage.IsSampler()) {
         ast_handle_type =
-            ty_.Sampler(usage.IsComparisonSampler() ? ast::SamplerKind::kComparisonSampler
-                                                    : ast::SamplerKind::kSampler);
+            ty_.Sampler(usage.IsComparisonSampler() ? type::SamplerKind::kComparisonSampler
+                                                    : type::SamplerKind::kSampler);
     } else if (usage.IsTexture()) {
         const spvtools::opt::analysis::Image* image_type =
             type_mgr_->GetType(raw_handle_type->result_id())->AsImage();
diff --git a/src/tint/reader/spirv/parser_type.cc b/src/tint/reader/spirv/parser_type.cc
index 5751866..40245ca 100644
--- a/src/tint/reader/spirv/parser_type.cc
+++ b/src/tint/reader/spirv/parser_type.cc
@@ -216,7 +216,7 @@
     }
 }
 
-Sampler::Sampler(ast::SamplerKind k) : kind(k) {}
+Sampler::Sampler(type::SamplerKind k) : kind(k) {}
 Sampler::Sampler(const Sampler&) = default;
 
 const ast::Type* Sampler::Build(ProgramBuilder& b) const {
@@ -491,7 +491,7 @@
     return state->structs_.Get(name, std::move(members));
 }
 
-const spirv::Sampler* TypeManager::Sampler(ast::SamplerKind kind) {
+const spirv::Sampler* TypeManager::Sampler(type::SamplerKind kind) {
     return state->samplers_.Get(kind);
 }
 
@@ -574,9 +574,9 @@
 
 std::string Sampler::String() const {
     switch (kind) {
-        case ast::SamplerKind::kSampler:
+        case type::SamplerKind::kSampler:
             return "sampler";
-        case ast::SamplerKind::kComparisonSampler:
+        case type::SamplerKind::kComparisonSampler:
             return "sampler_comparison";
     }
     return "<unknown sampler>";
diff --git a/src/tint/reader/spirv/parser_type.h b/src/tint/reader/spirv/parser_type.h
index 0cdafeb..bf36af9 100644
--- a/src/tint/reader/spirv/parser_type.h
+++ b/src/tint/reader/spirv/parser_type.h
@@ -306,7 +306,7 @@
 struct Sampler final : public Castable<Sampler, Type> {
     /// Constructor
     /// @param k the sampler kind
-    explicit Sampler(ast::SamplerKind k);
+    explicit Sampler(type::SamplerKind k);
 
     /// Copy constructor
     /// @param other the other type to copy
@@ -322,7 +322,7 @@
 #endif  // NDEBUG
 
     /// the sampler kind
-    ast::SamplerKind const kind;
+    type::SamplerKind const kind;
 };
 
 /// Base class for texture types
@@ -591,7 +591,7 @@
     /// @param k the sampler kind
     /// @return a Sampler type. Repeated calls with the same arguments will return
     /// the same pointer.
-    const spirv::Sampler* Sampler(ast::SamplerKind k);
+    const spirv::Sampler* Sampler(type::SamplerKind k);
     /// @param d the texture dimensions
     /// @return a DepthTexture type. Repeated calls with the same arguments will
     /// return the same pointer.
diff --git a/src/tint/reader/spirv/parser_type_test.cc b/src/tint/reader/spirv/parser_type_test.cc
index 183636f..232c2c5 100644
--- a/src/tint/reader/spirv/parser_type_test.cc
+++ b/src/tint/reader/spirv/parser_type_test.cc
@@ -36,7 +36,7 @@
     EXPECT_EQ(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 3, 2));
     EXPECT_EQ(ty.Alias(sym, ty.I32()), ty.Alias(sym, ty.I32()));
     EXPECT_EQ(ty.Struct(sym, {ty.I32()}), ty.Struct(sym, {ty.I32()}));
-    EXPECT_EQ(ty.Sampler(ast::SamplerKind::kSampler), ty.Sampler(ast::SamplerKind::kSampler));
+    EXPECT_EQ(ty.Sampler(type::SamplerKind::kSampler), ty.Sampler(type::SamplerKind::kSampler));
     EXPECT_EQ(ty.DepthTexture(type::TextureDimension::k2d),
               ty.DepthTexture(type::TextureDimension::k2d));
     EXPECT_EQ(ty.MultisampledTexture(type::TextureDimension::k2d, ty.I32()),
@@ -68,8 +68,8 @@
     EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 3, 3));
     EXPECT_NE(ty.Alias(sym_a, ty.I32()), ty.Alias(sym_b, ty.I32()));
     EXPECT_NE(ty.Struct(sym_a, {ty.I32()}), ty.Struct(sym_b, {ty.I32()}));
-    EXPECT_NE(ty.Sampler(ast::SamplerKind::kSampler),
-              ty.Sampler(ast::SamplerKind::kComparisonSampler));
+    EXPECT_NE(ty.Sampler(type::SamplerKind::kSampler),
+              ty.Sampler(type::SamplerKind::kComparisonSampler));
     EXPECT_NE(ty.DepthTexture(type::TextureDimension::k2d),
               ty.DepthTexture(type::TextureDimension::k1d));
     EXPECT_NE(ty.MultisampledTexture(type::TextureDimension::k2d, ty.I32()),
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 89804f2..6371db7 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -96,9 +96,10 @@
     while (true) {
         tokens.emplace_back(next());
 
-        // If the token can be split, we insert a placeholder element into
-        // the stream to hold the split character.
-        if (tokens.back().IsSplittable()) {
+        // If the token can be split, we insert a placeholder element(s) into the stream to hold the
+        // split character.
+        size_t num_placeholders = tokens.back().NumPlaceholders();
+        for (size_t i = 0; i < num_placeholders; i++) {
             auto src = tokens.back().source();
             src.range.begin.column++;
             tokens.emplace_back(Token(Token::Type::kPlaceholder, src));
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index ffa85bd..a1a4764 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -989,7 +989,6 @@
                                          TokenData{"&=", Token::Type::kAndEqual},
                                          TokenData{"|=", Token::Type::kOrEqual},
                                          TokenData{"^=", Token::Type::kXorEqual},
-                                         TokenData{">>=", Token::Type::kShiftRightEqual},
                                          TokenData{"<<=", Token::Type::kShiftLeftEqual}));
 
 using SplittablePunctuationTest = testing::TestWithParam<TokenData>;
@@ -1010,18 +1009,23 @@
         EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
     }
 
-    {
-        auto& t = list[1];
+    const size_t num_placeholders = list[0].NumPlaceholders();
+    EXPECT_GT(num_placeholders, 0u);
+    ASSERT_EQ(list.size(), 2u + num_placeholders);
+
+    for (size_t i = 0; i < num_placeholders; i++) {
+        auto& t = list[1 + i];
         EXPECT_TRUE(t.Is(Token::Type::kPlaceholder));
         EXPECT_EQ(t.source().range.begin.line, 1u);
-        EXPECT_EQ(t.source().range.begin.column, 2u);
+        EXPECT_EQ(t.source().range.begin.column, 2u + i);
         EXPECT_EQ(t.source().range.end.line, 1u);
         EXPECT_EQ(t.source().range.end.column, 1u + strlen(params.input));
     }
 
     {
-        auto& t = list[2];
-        EXPECT_EQ(t.source().range.begin.column, 1 + std::string(params.input).size());
+        auto& t = list.back();
+        EXPECT_TRUE(t.Is(Token::Type::kEOF));
+        EXPECT_EQ(t.source().range.begin.column, 2u + num_placeholders);
     }
 }
 INSTANTIATE_TEST_SUITE_P(LexerTest,
@@ -1029,7 +1033,8 @@
                          testing::Values(TokenData{"&&", Token::Type::kAndAnd},
                                          TokenData{">=", Token::Type::kGreaterThanEqual},
                                          TokenData{"--", Token::Type::kMinusMinus},
-                                         TokenData{">>", Token::Type::kShiftRight}));
+                                         TokenData{">>", Token::Type::kShiftRight},
+                                         TokenData{">>=", Token::Type::kShiftRightEqual}));
 
 using KeywordTest = testing::TestWithParam<TokenData>;
 TEST_P(KeywordTest, Parses) {
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 4ac0aa2..620dc1e 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -790,11 +790,11 @@
 Maybe<const ast::Type*> ParserImpl::sampler_type() {
     Source source;
     if (match(Token::Type::kSampler, &source)) {
-        return builder_.ty.sampler(source, ast::SamplerKind::kSampler);
+        return builder_.ty.sampler(source, type::SamplerKind::kSampler);
     }
 
     if (match(Token::Type::kComparisonSampler, &source)) {
-        return builder_.ty.sampler(source, ast::SamplerKind::kComparisonSampler);
+        return builder_.ty.sampler(source, type::SamplerKind::kComparisonSampler);
     }
 
     return Failure::kNoMatch;
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index 2aeffa9..1c8531b 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -379,10 +379,20 @@
         return type_ == Type::kVec2 || type_ == Type::kVec3 || type_ == Type::kVec4;
     }
 
-    /// @returns true if the token can be split during parse into component tokens
-    bool IsSplittable() const {
-        return Is(Type::kShiftRight) || Is(Type::kGreaterThanEqual) || Is(Type::kAndAnd) ||
-               Is(Type::kMinusMinus);
+    /// @returns the number of placeholder tokens required to follow the token, in order to provide
+    /// space for token splitting.
+    size_t NumPlaceholders() const {
+        switch (type_) {
+            case Type::kShiftRightEqual:
+                return 2;
+            case Type::kShiftRight:
+            case Type::kGreaterThanEqual:
+            case Type::kAndAnd:
+            case Type::kMinusMinus:
+                return 1;
+            default:
+                return 0;
+        }
     }
 
     /// @returns true if the token is a binary operator
diff --git a/src/tint/resolver/assignment_validation_test.cc b/src/tint/resolver/assignment_validation_test.cc
index 3fc596d..f868d42 100644
--- a/src/tint/resolver/assignment_validation_test.cc
+++ b/src/tint/resolver/assignment_validation_test.cc
@@ -363,7 +363,7 @@
                              });
     GlobalVar("tex", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), Group(0_a),
               Binding(0_a));
-    GlobalVar("smp", ty.sampler(ast::SamplerKind::kSampler), Group(0_a), Binding(1_a));
+    GlobalVar("smp", ty.sampler(type::SamplerKind::kSampler), Group(0_a), Binding(1_a));
     GlobalVar("u", ty.Of(U), type::AddressSpace::kUniform, Group(0_a), Binding(2_a));
     GlobalVar("s", ty.Of(S), type::AddressSpace::kStorage, Group(0_a), Binding(3_a));
     GlobalVar("wg", ty.array<f32, 10>(), type::AddressSpace::kWorkgroup);
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 00ee7aa..ca36e0b 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -507,7 +507,7 @@
 }
 
 TEST_F(EntryPointParameterAttributeTest, DuplicateInternalAttribute) {
-    auto* s = Param("s", ty.sampler(ast::SamplerKind::kSampler),
+    auto* s = Param("s", ty.sampler(type::SamplerKind::kSampler),
                     utils::Vector{
                         Binding(0_a),
                         Group(0_a),
@@ -873,7 +873,7 @@
     auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
     auto* attr = attrs[0];
     if (IsBindingAttribute(params.kind)) {
-        GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), attrs);
+        GlobalVar("a", ty.sampler(type::SamplerKind::kSampler), attrs);
     } else {
         GlobalVar("a", ty.f32(), type::AddressSpace::kPrivate, attrs);
     }
@@ -906,7 +906,7 @@
                                          TestParams{AttributeKind::kBindingAndGroup, true}));
 
 TEST_F(VariableAttributeTest, DuplicateAttribute) {
-    GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), Binding(Source{{12, 34}}, 2_a),
+    GlobalVar("a", ty.sampler(type::SamplerKind::kSampler), Binding(Source{{12, 34}}, 2_a),
               Group(2_a), Binding(Source{{56, 78}}, 3_a));
 
     EXPECT_FALSE(r()->Resolve());
@@ -1175,7 +1175,7 @@
 }
 
 TEST_F(ResourceAttributeTest, SamplerMissingBinding) {
-    GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler));
+    GlobalVar(Source{{12, 34}}, "G", ty.sampler(type::SamplerKind::kSampler));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1183,7 +1183,7 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPairMissingBinding) {
-    GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), Group(1_a));
+    GlobalVar(Source{{12, 34}}, "G", ty.sampler(type::SamplerKind::kSampler), Group(1_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1191,7 +1191,7 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPairMissingGroup) {
-    GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), Binding(1_a));
+    GlobalVar(Source{{12, 34}}, "G", ty.sampler(type::SamplerKind::kSampler), Binding(1_a));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index a380305..4047ce5 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -50,6 +50,7 @@
 #include "src/tint/ast/pointer.h"
 #include "src/tint/ast/return_statement.h"
 #include "src/tint/ast/sampled_texture.h"
+#include "src/tint/ast/sampler.h"
 #include "src/tint/ast/stage_attribute.h"
 #include "src/tint/ast/storage_texture.h"
 #include "src/tint/ast/stride_attribute.h"
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index e6f09a1..75faf34 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1298,7 +1298,7 @@
     GlobalVar(Sym(), ty.multisampled_texture(type::TextureDimension::k2d, T));
     GlobalVar(Sym(), ty.storage_texture(type::TextureDimension::k2d, type::TexelFormat::kR32Float,
                                         type::Access::kRead));  //
-    GlobalVar(Sym(), ty.sampler(ast::SamplerKind::kSampler));
+    GlobalVar(Sym(), ty.sampler(type::SamplerKind::kSampler));
 
     GlobalVar(Sym(), ty.i32(), utils::Vector{Binding(V), Group(V)});
     GlobalVar(Sym(), ty.i32(), utils::Vector{Location(V)});
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 3d51c63..0cd18e8 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -585,11 +585,11 @@
     if (ty->Is<Any>()) {
         return true;
     }
-    return ty->Is([](const type::Sampler* s) { return s->kind() == ast::SamplerKind::kSampler; });
+    return ty->Is([](const type::Sampler* s) { return s->kind() == type::SamplerKind::kSampler; });
 }
 
 const type::Sampler* build_sampler(MatchState& state) {
-    return state.builder.create<type::Sampler>(ast::SamplerKind::kSampler);
+    return state.builder.create<type::Sampler>(type::SamplerKind::kSampler);
 }
 
 bool match_sampler_comparison(MatchState&, const type::Type* ty) {
@@ -597,11 +597,11 @@
         return true;
     }
     return ty->Is(
-        [](const type::Sampler* s) { return s->kind() == ast::SamplerKind::kComparisonSampler; });
+        [](const type::Sampler* s) { return s->kind() == type::SamplerKind::kComparisonSampler; });
 }
 
 const type::Sampler* build_sampler_comparison(MatchState& state) {
-    return state.builder.create<type::Sampler>(ast::SamplerKind::kComparisonSampler);
+    return state.builder.create<type::Sampler>(type::SamplerKind::kComparisonSampler);
 }
 
 bool match_texture(MatchState&,
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index e0d32f3..c649c46 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -282,7 +282,7 @@
     auto* vec2_f32 = create<type::Vector>(f32, 2u);
     auto* vec4_f32 = create<type::Vector>(f32, 4u);
     auto* tex = create<type::SampledTexture>(type::TextureDimension::k2d, f32);
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
     auto result = table->Lookup(BuiltinType::kTextureSample, utils::Vector{tex, sampler, vec2_f32},
                                 sem::EvaluationStage::kConstant, Source{});
     ASSERT_NE(result.sem, nullptr) << Diagnostics().str();
diff --git a/src/tint/resolver/load_test.cc b/src/tint/resolver/load_test.cc
index a97b011..3e9d50c 100644
--- a/src/tint/resolver/load_test.cc
+++ b/src/tint/resolver/load_test.cc
@@ -221,11 +221,12 @@
     // f(t, s);
     GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()),
               utils::Vector{Group(0_a), Binding(0_a)});
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), utils::Vector{Group(0_a), Binding(1_a)});
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler),
+              utils::Vector{Group(0_a), Binding(1_a)});
     Func("f",
          utils::Vector{
              Param("tp", ty.sampled_texture(type::TextureDimension::k2d, ty.f32())),
-             Param("sp", ty.sampler(ast::SamplerKind::kSampler)),
+             Param("sp", ty.sampler(type::SamplerKind::kSampler)),
          },
          ty.vec4<f32>(),
          utils::Vector{
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 8be16c1..0c0a344 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -1878,7 +1878,7 @@
 }
 
 TEST_F(ResolverTest, AddressSpace_SetForSampler) {
-    auto* t = ty.sampler(ast::SamplerKind::kSampler);
+    auto* t = ty.sampler(type::SamplerKind::kSampler);
     auto* var = GlobalVar("var", t, Binding(0_a), Group(0_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -1920,8 +1920,8 @@
 TEST_F(ResolverTest, BindingPoint_SetForResources) {
     // @group(1) @binding(2) var s1 : sampler;
     // @group(3) @binding(4) var s2 : sampler;
-    auto* s1 = GlobalVar(Sym(), ty.sampler(ast::SamplerKind::kSampler), Group(1_a), Binding(2_a));
-    auto* s2 = GlobalVar(Sym(), ty.sampler(ast::SamplerKind::kSampler), Group(3_a), Binding(4_a));
+    auto* s1 = GlobalVar(Sym(), ty.sampler(type::SamplerKind::kSampler), Group(1_a), Binding(2_a));
+    auto* s2 = GlobalVar(Sym(), ty.sampler(type::SamplerKind::kSampler), Group(3_a), Binding(4_a));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -2126,7 +2126,7 @@
 TEST_F(ResolverTest, TextureSampler_TextureSample) {
     GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), Group(1_a),
               Binding(1_a));
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(1_a), Binding(2_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(1_a), Binding(2_a));
 
     auto* call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* f = Func("test_function", utils::Empty, ty.void_(), utils::Vector{call},
@@ -2144,7 +2144,7 @@
 TEST_F(ResolverTest, TextureSampler_TextureSampleInFunction) {
     GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), Group(1_a),
               Binding(1_a));
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(1_a), Binding(2_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(1_a), Binding(2_a));
 
     auto* inner_call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* inner_func =
@@ -2170,7 +2170,7 @@
 TEST_F(ResolverTest, TextureSampler_TextureSampleFunctionDiamondSameVariables) {
     GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), Group(1_a),
               Binding(1_a));
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(1_a), Binding(2_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(1_a), Binding(2_a));
 
     auto* inner_call_1 = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* inner_func_1 =
@@ -2207,7 +2207,7 @@
               Binding(1_a));
     GlobalVar("t2", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), Group(1_a),
               Binding(2_a));
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(1_a), Binding(3_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(1_a), Binding(3_a));
 
     auto* inner_call_1 = CallStmt(Call("textureSample", "t1", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* inner_func_1 =
@@ -2270,7 +2270,7 @@
     // fn helper(sl: ptr<function, sampler>, tl: ptr<function, texture_2d<f32>>) -> vec4<f32> {
     //     return textureSampleLevel(*tl, *sl, c, 0.0);
     // }
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(0_a), Binding(0_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(0_a), Binding(0_a));
     GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), Group(0_a),
               Binding(1_a));
     GlobalVar("c", ty.vec2<f32>(), type::AddressSpace::kUniform, Group(0_a), Binding(2_a));
@@ -2288,7 +2288,7 @@
 
     Func("helper",
          utils::Vector{
-             Param("sl", ty.pointer(ty.sampler(ast::SamplerKind::kSampler),
+             Param("sl", ty.pointer(ty.sampler(type::SamplerKind::kSampler),
                                     type::AddressSpace::kFunction)),
              Param("tl", ty.pointer(ty.sampled_texture(type::TextureDimension::k2d, ty.f32()),
                                     type::AddressSpace::kFunction)),
diff --git a/src/tint/resolver/side_effects_test.cc b/src/tint/resolver/side_effects_test.cc
index 2fb95e2..081d97c 100644
--- a/src/tint/resolver/side_effects_test.cc
+++ b/src/tint/resolver/side_effects_test.cc
@@ -193,9 +193,9 @@
                   ty.storage_texture(type::TextureDimension::k2d, type::TexelFormat::kR32Float,
                                      type::Access::kWrite),
                   Group(0_a), Binding(AInt(next_binding++)));
-        GlobalVar("s2d", ty.sampler(ast::SamplerKind::kSampler), Group(0_a),
+        GlobalVar("s2d", ty.sampler(type::SamplerKind::kSampler), Group(0_a),
                   Binding(AInt(next_binding++)));
-        GlobalVar("scomp", ty.sampler(ast::SamplerKind::kComparisonSampler), Group(0_a),
+        GlobalVar("scomp", ty.sampler(type::SamplerKind::kComparisonSampler), Group(0_a),
                   Binding(AInt(next_binding++)));
     }
 
diff --git a/src/tint/resolver/type_initializer_validation_test.cc b/src/tint/resolver/type_initializer_validation_test.cc
index 27b6dc1..c8c0a3c 100644
--- a/src/tint/resolver/type_initializer_validation_test.cc
+++ b/src/tint/resolver/type_initializer_validation_test.cc
@@ -3210,7 +3210,7 @@
 
 TEST_F(ResolverTypeInitializerValidationTest, NonConstructibleType_Sampler) {
     WrapInFunction(
-        Assign(Phony(), Construct(Source{{12, 34}}, ty.sampler(ast::SamplerKind::kSampler))));
+        Assign(Phony(), Construct(Source{{12, 34}}, ty.sampler(type::SamplerKind::kSampler))));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 67a26c7..36eec01 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -336,7 +336,7 @@
 }
 
 TEST_F(ResolverValidationTest, AddressSpace_SamplerExplicitAddressSpace) {
-    auto* t = ty.sampler(ast::SamplerKind::kSampler);
+    auto* t = ty.sampler(type::SamplerKind::kSampler);
     GlobalVar(Source{{12, 34}}, "var", t, type::AddressSpace::kHandle, Binding(0_a), Group(0_a));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 0906322..5434042 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -349,7 +349,7 @@
     // fn foo() {
     //   var v = s;
     // }
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(0_a), Binding(0_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(0_a), Binding(0_a));
     auto* v = Var(Source{{12, 34}}, "v", Expr("s"));
     WrapInFunction(v);
 
diff --git a/src/tint/sem/function.cc b/src/tint/sem/function.cc
index dabf1ae..3ce22a1 100644
--- a/src/tint/sem/function.cc
+++ b/src/tint/sem/function.cc
@@ -110,11 +110,11 @@
 }
 
 Function::VariableBindings Function::TransitivelyReferencedSamplerVariables() const {
-    return TransitivelyReferencedSamplerVariablesImpl(ast::SamplerKind::kSampler);
+    return TransitivelyReferencedSamplerVariablesImpl(type::SamplerKind::kSampler);
 }
 
 Function::VariableBindings Function::TransitivelyReferencedComparisonSamplerVariables() const {
-    return TransitivelyReferencedSamplerVariablesImpl(ast::SamplerKind::kComparisonSampler);
+    return TransitivelyReferencedSamplerVariablesImpl(type::SamplerKind::kComparisonSampler);
 }
 
 Function::VariableBindings Function::TransitivelyReferencedSampledTextureVariables() const {
@@ -149,7 +149,7 @@
 }
 
 Function::VariableBindings Function::TransitivelyReferencedSamplerVariablesImpl(
-    ast::SamplerKind kind) const {
+    type::SamplerKind kind) const {
     VariableBindings ret;
 
     for (auto* global : TransitivelyReferencedGlobals()) {
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index 596b3d9..32468aa 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -260,7 +260,7 @@
     Function(const Function&) = delete;
     Function(Function&&) = delete;
 
-    VariableBindings TransitivelyReferencedSamplerVariablesImpl(ast::SamplerKind kind) const;
+    VariableBindings TransitivelyReferencedSamplerVariablesImpl(type::SamplerKind kind) const;
     VariableBindings TransitivelyReferencedSampledTextureVariablesImpl(bool multisampled) const;
 
     const ast::Function* const declaration_;
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index 4c196af..3323b28 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -640,7 +640,7 @@
         b.Func(name,
                utils::Vector{
                    b.Param("t", b.ty.sampled_texture(type::TextureDimension::k2d, b.ty.f32())),
-                   b.Param("s", b.ty.sampler(ast::SamplerKind::kSampler)),
+                   b.Param("s", b.ty.sampler(type::SamplerKind::kSampler)),
                    b.Param("coord", b.ty.vec2<f32>()),
                },
                b.ty.vec4<f32>(), body);
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index ca42335..65bf161 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -123,9 +123,9 @@
     /// Creates placeholder global sampler variables.
     /// @param kind the sampler kind to create for
     /// @returns the newly-created global variable
-    const ast::Variable* CreatePlaceholder(ast::SamplerKind kind) {
+    const ast::Variable* CreatePlaceholder(type::SamplerKind kind) {
         const ast::Type* type = ctx.dst->ty.sampler(kind);
-        const char* name = kind == ast::SamplerKind::kComparisonSampler
+        const char* name = kind == type::SamplerKind::kComparisonSampler
                                ? "placeholder_comparison_sampler"
                                : "placeholder_sampler";
         Symbol symbol = ctx.dst->Symbols().New(name);
@@ -265,8 +265,8 @@
                                                                          [new_pair];
                             args.Push(ctx.dst->Expr(var->symbol));
                         } else if (auto* sampler_type = type->As<type::Sampler>()) {
-                            ast::SamplerKind kind = sampler_type->kind();
-                            int index = (kind == ast::SamplerKind::kSampler) ? 0 : 1;
+                            type::SamplerKind kind = sampler_type->kind();
+                            int index = (kind == type::SamplerKind::kSampler) ? 0 : 1;
                             const ast::Variable*& p = placeholder_samplers_[index];
                             if (!p) {
                                 p = CreatePlaceholder(kind);
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 28fe27b..9ef4443 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -426,7 +426,7 @@
                                b.ty.sampled_texture(type::TextureDimension::k2d, b.ty.f32())),
                        b.Param("plane1",
                                b.ty.sampled_texture(type::TextureDimension::k2d, b.ty.f32())),
-                       b.Param("smp", b.ty.sampler(ast::SamplerKind::kSampler)),
+                       b.Param("smp", b.ty.sampler(type::SamplerKind::kSampler)),
                        b.Param("coord", b.ty.vec2(b.ty.f32())),
                        b.Param("params", b.ty.type_name(params_struct_sym)),
                    },
diff --git a/src/tint/type/abstract_float.cc b/src/tint/type/abstract_float.cc
index f3032e0..46cbf15 100644
--- a/src/tint/type/abstract_float.cc
+++ b/src/tint/type/abstract_float.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/abstract_float.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/utils/hash.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::AbstractFloat);
diff --git a/src/tint/type/abstract_int.cc b/src/tint/type/abstract_int.cc
index 806cdd3..4b40937 100644
--- a/src/tint/type/abstract_int.cc
+++ b/src/tint/type/abstract_int.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/abstract_int.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/utils/hash.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::AbstractInt);
diff --git a/src/tint/type/atomic.cc b/src/tint/type/atomic.cc
index 0daa7f5..0459394 100644
--- a/src/tint/type/atomic.cc
+++ b/src/tint/type/atomic.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/atomic.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/reference.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/bool.cc b/src/tint/type/bool.cc
index 4c3849c..31fcb27 100644
--- a/src/tint/type/bool.cc
+++ b/src/tint/type/bool.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/bool.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::Bool);
 
diff --git a/src/tint/type/depth_multisampled_texture.cc b/src/tint/type/depth_multisampled_texture.cc
index 43d30a8..84d7c32 100644
--- a/src/tint/type/depth_multisampled_texture.cc
+++ b/src/tint/type/depth_multisampled_texture.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/depth_multisampled_texture.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/texture_dimension.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/depth_texture.cc b/src/tint/type/depth_texture.cc
index 214b407..ca216e7 100644
--- a/src/tint/type/depth_texture.cc
+++ b/src/tint/type/depth_texture.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/depth_texture.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/texture_dimension.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/external_texture.cc b/src/tint/type/external_texture.cc
index bfd2edc..986c2b4 100644
--- a/src/tint/type/external_texture.cc
+++ b/src/tint/type/external_texture.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/external_texture.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/texture_dimension.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::ExternalTexture);
diff --git a/src/tint/type/f16.cc b/src/tint/type/f16.cc
index 5ea9aca..45b88dd 100644
--- a/src/tint/type/f16.cc
+++ b/src/tint/type/f16.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/f16.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::F16);
 
diff --git a/src/tint/type/f32.cc b/src/tint/type/f32.cc
index 91872a2..8af826c 100644
--- a/src/tint/type/f32.cc
+++ b/src/tint/type/f32.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/f32.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::F32);
 
diff --git a/src/tint/type/i32.cc b/src/tint/type/i32.cc
index 9fe0d95..9ca1c69 100644
--- a/src/tint/type/i32.cc
+++ b/src/tint/type/i32.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/i32.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::I32);
 
diff --git a/src/tint/type/matrix.cc b/src/tint/type/matrix.cc
index 600f4b0..76970e6 100644
--- a/src/tint/type/matrix.cc
+++ b/src/tint/type/matrix.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/matrix.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/vector.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/multisampled_texture.cc b/src/tint/type/multisampled_texture.cc
index 801a891..182ae88 100644
--- a/src/tint/type/multisampled_texture.cc
+++ b/src/tint/type/multisampled_texture.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/multisampled_texture.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/texture_dimension.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/pointer.cc b/src/tint/type/pointer.cc
index f00f4fb..809d05b 100644
--- a/src/tint/type/pointer.cc
+++ b/src/tint/type/pointer.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/pointer.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/reference.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/reference.cc b/src/tint/type/reference.cc
index 090d8d7..5951f38 100644
--- a/src/tint/type/reference.cc
+++ b/src/tint/type/reference.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/reference.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/utils/hash.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::Reference);
diff --git a/src/tint/type/sampled_texture.cc b/src/tint/type/sampled_texture.cc
index 323aaa4..b3e7375 100644
--- a/src/tint/type/sampled_texture.cc
+++ b/src/tint/type/sampled_texture.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/sampled_texture.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/type/texture_dimension.h"
 #include "src/tint/utils/hash.h"
 
diff --git a/src/tint/type/sampler.cc b/src/tint/type/sampler.cc
index 50d3f28..b7e0aa2 100644
--- a/src/tint/type/sampler.cc
+++ b/src/tint/type/sampler.cc
@@ -14,14 +14,14 @@
 
 #include "src/tint/type/sampler.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/utils/hash.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::Sampler);
 
 namespace tint::type {
 
-Sampler::Sampler(ast::SamplerKind kind)
+Sampler::Sampler(SamplerKind kind)
     : Base(utils::Hash(TypeInfo::Of<Sampler>().full_hashcode, kind), type::Flags{}), kind_(kind) {}
 
 Sampler::~Sampler() = default;
@@ -34,7 +34,7 @@
 }
 
 std::string Sampler::FriendlyName(const SymbolTable&) const {
-    return kind_ == ast::SamplerKind::kSampler ? "sampler" : "sampler_comparison";
+    return kind_ == SamplerKind::kSampler ? "sampler" : "sampler_comparison";
 }
 
 Sampler* Sampler::Clone(CloneContext& ctx) const {
diff --git a/src/tint/type/sampler.h b/src/tint/type/sampler.h
index ab81073..fb2ebb4 100644
--- a/src/tint/type/sampler.h
+++ b/src/tint/type/sampler.h
@@ -17,7 +17,7 @@
 
 #include <string>
 
-#include "src/tint/ast/sampler.h"
+#include "src/tint/type/sampler_kind.h"
 #include "src/tint/type/type.h"
 
 namespace tint::type {
@@ -27,7 +27,7 @@
   public:
     /// Constructor
     /// @param kind the kind of sampler
-    explicit Sampler(ast::SamplerKind kind);
+    explicit Sampler(SamplerKind kind);
 
     /// Destructor
     ~Sampler() override;
@@ -37,10 +37,10 @@
     bool Equals(const UniqueNode& other) const override;
 
     /// @returns the sampler type
-    ast::SamplerKind kind() const { return kind_; }
+    SamplerKind kind() const { return kind_; }
 
     /// @returns true if this is a comparison sampler
-    bool IsComparison() const { return kind_ == ast::SamplerKind::kComparisonSampler; }
+    bool IsComparison() const { return kind_ == SamplerKind::kComparisonSampler; }
 
     /// @param symbols the program's symbol table
     /// @returns the name for this type that closely resembles how it would be
@@ -52,7 +52,7 @@
     Sampler* Clone(CloneContext& ctx) const override;
 
   private:
-    ast::SamplerKind const kind_;
+    SamplerKind const kind_;
 };
 
 }  // namespace tint::type
diff --git a/src/tint/type/sampler_kind.cc b/src/tint/type/sampler_kind.cc
new file mode 100644
index 0000000..4e20700
--- /dev/null
+++ b/src/tint/type/sampler_kind.cc
@@ -0,0 +1,31 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/type/sampler_kind.h"
+
+namespace tint::type {
+
+std::ostream& operator<<(std::ostream& out, SamplerKind kind) {
+    switch (kind) {
+        case SamplerKind::kSampler:
+            out << "sampler";
+            break;
+        case SamplerKind::kComparisonSampler:
+            out << "comparison_sampler";
+            break;
+    }
+    return out;
+}
+
+}  // namespace tint::type
diff --git a/src/tint/type/sampler_kind.h b/src/tint/type/sampler_kind.h
new file mode 100644
index 0000000..3522fda
--- /dev/null
+++ b/src/tint/type/sampler_kind.h
@@ -0,0 +1,37 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_TYPE_SAMPLER_KIND_H_
+#define SRC_TINT_TYPE_SAMPLER_KIND_H_
+
+#include <ostream>
+
+namespace tint::type {
+
+/// The different kinds of samplers
+enum class SamplerKind {
+    /// A regular sampler
+    kSampler,
+    /// A comparison sampler
+    kComparisonSampler
+};
+
+/// @param out the std::ostream to write to
+/// @param kind the SamplerKind
+/// @return the std::ostream so calls can be chained
+std::ostream& operator<<(std::ostream& out, SamplerKind kind);
+
+}  // namespace tint::type
+
+#endif  // SRC_TINT_TYPE_SAMPLER_KIND_H_
diff --git a/src/tint/type/sampler_test.cc b/src/tint/type/sampler_test.cc
index ef0bdf5..8323e29 100644
--- a/src/tint/type/sampler_test.cc
+++ b/src/tint/type/sampler_test.cc
@@ -22,12 +22,12 @@
 using SamplerTest = TestHelper;
 
 TEST_F(SamplerTest, Creation) {
-    auto* a = create<Sampler>(ast::SamplerKind::kSampler);
-    auto* b = create<Sampler>(ast::SamplerKind::kSampler);
-    auto* c = create<Sampler>(ast::SamplerKind::kComparisonSampler);
+    auto* a = create<Sampler>(SamplerKind::kSampler);
+    auto* b = create<Sampler>(SamplerKind::kSampler);
+    auto* c = create<Sampler>(SamplerKind::kComparisonSampler);
 
-    EXPECT_EQ(a->kind(), ast::SamplerKind::kSampler);
-    EXPECT_EQ(c->kind(), ast::SamplerKind::kComparisonSampler);
+    EXPECT_EQ(a->kind(), SamplerKind::kSampler);
+    EXPECT_EQ(c->kind(), SamplerKind::kComparisonSampler);
 
     EXPECT_FALSE(a->IsComparison());
     EXPECT_TRUE(c->IsComparison());
@@ -37,16 +37,16 @@
 }
 
 TEST_F(SamplerTest, Hash) {
-    auto* a = create<Sampler>(ast::SamplerKind::kSampler);
-    auto* b = create<Sampler>(ast::SamplerKind::kSampler);
+    auto* a = create<Sampler>(SamplerKind::kSampler);
+    auto* b = create<Sampler>(SamplerKind::kSampler);
 
     EXPECT_EQ(a->unique_hash, b->unique_hash);
 }
 
 TEST_F(SamplerTest, Equals) {
-    auto* a = create<Sampler>(ast::SamplerKind::kSampler);
-    auto* b = create<Sampler>(ast::SamplerKind::kSampler);
-    auto* c = create<Sampler>(ast::SamplerKind::kComparisonSampler);
+    auto* a = create<Sampler>(SamplerKind::kSampler);
+    auto* b = create<Sampler>(SamplerKind::kSampler);
+    auto* c = create<Sampler>(SamplerKind::kComparisonSampler);
 
     EXPECT_TRUE(a->Equals(*b));
     EXPECT_FALSE(a->Equals(*c));
@@ -54,23 +54,23 @@
 }
 
 TEST_F(SamplerTest, FriendlyNameSampler) {
-    Sampler s{ast::SamplerKind::kSampler};
+    Sampler s{SamplerKind::kSampler};
     EXPECT_EQ(s.FriendlyName(Symbols()), "sampler");
 }
 
 TEST_F(SamplerTest, FriendlyNameComparisonSampler) {
-    Sampler s{ast::SamplerKind::kComparisonSampler};
+    Sampler s{SamplerKind::kComparisonSampler};
     EXPECT_EQ(s.FriendlyName(Symbols()), "sampler_comparison");
 }
 
 TEST_F(SamplerTest, Clone) {
-    auto* a = create<Sampler>(ast::SamplerKind::kSampler);
+    auto* a = create<Sampler>(SamplerKind::kSampler);
 
     type::Manager mgr;
     type::CloneContext ctx{{nullptr}, {nullptr, &mgr}};
 
     auto* mt = a->Clone(ctx);
-    EXPECT_EQ(mt->kind(), ast::SamplerKind::kSampler);
+    EXPECT_EQ(mt->kind(), SamplerKind::kSampler);
 }
 
 }  // namespace
diff --git a/src/tint/type/storage_texture.cc b/src/tint/type/storage_texture.cc
index 427a063..25beb74 100644
--- a/src/tint/type/storage_texture.cc
+++ b/src/tint/type/storage_texture.cc
@@ -14,7 +14,10 @@
 
 #include "src/tint/type/storage_texture.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/f32.h"
+#include "src/tint/type/i32.h"
+#include "src/tint/type/manager.h"
+#include "src/tint/type/u32.h"
 #include "src/tint/utils/hash.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::StorageTexture);
diff --git a/src/tint/type/u32.cc b/src/tint/type/u32.cc
index a749f85..249a47d 100644
--- a/src/tint/type/u32.cc
+++ b/src/tint/type/u32.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/u32.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::U32);
 
diff --git a/src/tint/type/vector.cc b/src/tint/type/vector.cc
index acd952c..146c1f4 100644
--- a/src/tint/type/vector.cc
+++ b/src/tint/type/vector.cc
@@ -14,7 +14,9 @@
 
 #include "src/tint/type/vector.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/debug.h"
+#include "src/tint/diagnostic/diagnostic.h"
+#include "src/tint/type/manager.h"
 #include "src/tint/utils/hash.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::Vector);
diff --git a/src/tint/type/void.cc b/src/tint/type/void.cc
index e29def1..bfe2baf 100644
--- a/src/tint/type/void.cc
+++ b/src/tint/type/void.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/type/void.h"
 
-#include "src/tint/program_builder.h"
+#include "src/tint/type/manager.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::type::Void);
 
diff --git a/src/tint/writer/flatten_bindings_test.cc b/src/tint/writer/flatten_bindings_test.cc
index ff16ed8..0464742 100644
--- a/src/tint/writer/flatten_bindings_test.cc
+++ b/src/tint/writer/flatten_bindings_test.cc
@@ -91,8 +91,9 @@
                 b.Group(2_a), b.Binding(2_a));
 
     const size_t num_samplers = 2;
-    b.GlobalVar("sampler1", b.ty.sampler(ast::SamplerKind::kSampler), b.Group(3_a), b.Binding(3_a));
-    b.GlobalVar("sampler2", b.ty.sampler(ast::SamplerKind::kComparisonSampler), b.Group(4_a),
+    b.GlobalVar("sampler1", b.ty.sampler(type::SamplerKind::kSampler), b.Group(3_a),
+                b.Binding(3_a));
+    b.GlobalVar("sampler2", b.ty.sampler(type::SamplerKind::kComparisonSampler), b.Group(4_a),
                 b.Binding(4_a));
 
     const size_t num_textures = 6;
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index fe4c0ab..5a63fcb 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -280,7 +280,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Type, EmitSampler) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
 
     GeneratorImpl& gen = Build();
 
@@ -291,7 +291,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Type, EmitSamplerComparison) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kComparisonSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kComparisonSampler);
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/hlsl/generator_impl_type_test.cc b/src/tint/writer/hlsl/generator_impl_type_test.cc
index 493bc2a..9f5d012 100644
--- a/src/tint/writer/hlsl/generator_impl_type_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_type_test.cc
@@ -274,7 +274,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitSampler) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
 
     GeneratorImpl& gen = Build();
 
@@ -285,7 +285,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Type, EmitSamplerComparison) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kComparisonSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kComparisonSampler);
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index 057b9e1..c116e87 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -731,7 +731,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Sampler) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
 
     GeneratorImpl& gen = Build();
 
@@ -741,7 +741,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_SamplerComparison) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kComparisonSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kComparisonSampler);
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index abab05b..9df433b 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -3735,11 +3735,11 @@
 
                 // Register both of the sampler type names. In SPIR-V they're the same
                 // sampler type, so we need to match that when we do the dedup check.
-                if (s->kind() == ast::SamplerKind::kSampler) {
+                if (s->kind() == type::SamplerKind::kSampler) {
                     type_to_id_[builder_.create<type::Sampler>(
-                        ast::SamplerKind::kComparisonSampler)] = id;
+                        type::SamplerKind::kComparisonSampler)] = id;
                 } else {
-                    type_to_id_[builder_.create<type::Sampler>(ast::SamplerKind::kSampler)] = id;
+                    type_to_id_[builder_.create<type::Sampler>(type::SamplerKind::kSampler)] = id;
                 }
                 return true;
             },
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index aa154b0..ac1dce3 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -41,7 +41,7 @@
 
 // This tests that we do not push OpTypeSampledImage and float_0 type twice.
 TEST_F(BuiltinBuilderTest, Call_TextureSampleCompare_Twice) {
-    auto* s = ty.sampler(ast::SamplerKind::kComparisonSampler);
+    auto* s = ty.sampler(type::SamplerKind::kComparisonSampler);
     auto* t = ty.depth_texture(type::TextureDimension::k2d);
 
     auto* tex = GlobalVar("texture", t, Binding(0_a), Group(0_a));
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index e6f4733..1331471 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -234,7 +234,7 @@
 }
 
 TEST_F(BuilderTest, GlobalVar_WithBindingAndGroup) {
-    auto* v = GlobalVar("var", ty.sampler(ast::SamplerKind::kSampler), Binding(2_a), Group(3_a));
+    auto* v = GlobalVar("var", ty.sampler(type::SamplerKind::kSampler), Binding(2_a), Group(3_a));
 
     spirv::Builder& b = Build();
 
diff --git a/src/tint/writer/spirv/builder_type_test.cc b/src/tint/writer/spirv/builder_type_test.cc
index 64f6f98..d80bdc4 100644
--- a/src/tint/writer/spirv/builder_type_test.cc
+++ b/src/tint/writer/spirv/builder_type_test.cc
@@ -965,7 +965,7 @@
 }
 
 TEST_F(BuilderTest_Type, Sampler) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
 
     spirv::Builder& b = Build();
 
@@ -975,7 +975,7 @@
 }
 
 TEST_F(BuilderTest_Type, ComparisonSampler) {
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kComparisonSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kComparisonSampler);
 
     spirv::Builder& b = Build();
 
@@ -985,8 +985,8 @@
 }
 
 TEST_F(BuilderTest_Type, Dedup_Sampler_And_ComparisonSampler) {
-    auto* comp_sampler = create<type::Sampler>(ast::SamplerKind::kComparisonSampler);
-    auto* sampler = create<type::Sampler>(ast::SamplerKind::kSampler);
+    auto* comp_sampler = create<type::Sampler>(type::SamplerKind::kComparisonSampler);
+    auto* sampler = create<type::Sampler>(type::SamplerKind::kSampler);
 
     spirv::Builder& b = Build();
 
diff --git a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
index 65366c8..294b2d6 100644
--- a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
@@ -106,7 +106,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Global_Sampler) {
-    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), Group(0_a), Binding(0_a));
+    GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), Group(0_a), Binding(0_a));
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/wgsl/generator_impl_type_test.cc b/src/tint/writer/wgsl/generator_impl_type_test.cc
index 8725d0e..cd2c7f0 100644
--- a/src/tint/writer/wgsl/generator_impl_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_type_test.cc
@@ -528,7 +528,7 @@
                     ImageFormatData{type::TexelFormat::kRgba32Float, "rgba32float"}));
 
 TEST_F(WgslGeneratorImplTest, EmitType_Sampler) {
-    auto* sampler = ty.sampler(ast::SamplerKind::kSampler);
+    auto* sampler = ty.sampler(type::SamplerKind::kSampler);
     Alias("make_type_reachable", sampler);
 
     GeneratorImpl& gen = Build();
@@ -539,7 +539,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitType_SamplerComparison) {
-    auto* sampler = ty.sampler(ast::SamplerKind::kComparisonSampler);
+    auto* sampler = ty.sampler(type::SamplerKind::kComparisonSampler);
     Alias("make_type_reachable", sampler);
 
     GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/wgsl/generator_impl_variable_test.cc b/src/tint/writer/wgsl/generator_impl_variable_test.cc
index e215d29..3d39ce9 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_test.cc
@@ -66,7 +66,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated) {
-    auto* v = GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), Group(1_a), Binding(2_a));
+    auto* v = GlobalVar("a", ty.sampler(type::SamplerKind::kSampler), Group(1_a), Binding(2_a));
 
     GeneratorImpl& gen = Build();