Import Tint changes from Dawn

Changes:
  - c0d51f1cd8945d035ed9ff83bd37d89cbf7e3de2 tint: spirv reader: detect and replace loads and stores o... by Antonio Maiorano <amaiorano@google.com>
  - c7d6ab6c5af7b3da8665ebac74408d9d5dcbd96d tint: Prevent function calls at module-scope by Ben Clayton <bclayton@google.com>
  - aa037ac489cde4ae1ec289e7927821c89d6c579d tint: Refactor sem::Constant to be less memory-hungry by Ben Clayton <bclayton@google.com>
  - cf52af7b0f2d5bf7764ff9c3840c1ac38fb1ccb9 tint: Have Number equality consider sign by Ben Clayton <bclayton@google.com>
  - 3a2a27971487769c4581c876ea379854a4fe997a Fixup various warnings in Tint which were accidentally su... by dan sinclair <dsinclair@chromium.org>
  - 2b4df7889186d2e98e437a2d1bb3e083b4722edb tint/number: Fix CheckedConvert() logic by Ben Clayton <bclayton@google.com>
  - 72876c14c3dc5fb947da619ef1fbb4faa9e98a3e tint/sem/constant.h: Remove #include to program_builder.h by Ben Clayton <bclayton@google.com>
  - c64ca23d94aae25c5573bb4ad354a8686fba285c tint: Deprecated module-scope 'let' for 'const' by Ben Clayton <bclayton@google.com>
  - d23f296a9a31daa764d348169d3ae13d5a09877b tint: Implement acosh, asinh, atanh by dan sinclair <dsinclair@chromium.org>
  - 6058882d4bf54d47d79ad528561ac89f66ec199a tint/resolver: Add f16 types, constructor, and conversions by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - 53af15836660ebd851702ebc1551dca0d0447249 tint/reader: Allow module-scope 'var' type inferencing by Ben Clayton <bclayton@google.com>
  - 19576e9015d2b2ce7a1933ec14606748a6902ead tint/writer: Handle and emit 'const' variables by Ben Clayton <bclayton@google.com>
  - 6c167a0dc78380dfc5d60623aa435af80066b910 msl: Promote local private vars to function scope by dan sinclair <dsinclair@chromium.org>
  - 7ebcfc7e0e2ad76202a77325d1e8dd82322c5d2a tint/transform: PromoteInitializersToLet by Ben Clayton <bclayton@google.com>
  - ce466c0df3d8474e61dff2315aa8de1c722d82eb tint: spir-v writer: Fix matrix constructor from matrix v... by Antonio Maiorano <amaiorano@google.com>
  - 54b3da95f808f14b913cbfe8132e7977246f443b tint/transform: Handle 'const' for Unshadow. by Ben Clayton <bclayton@google.com>
  - fcf9fdcad813691915fc313a7a1af1bbf3d9523f tint/writer/wgsl: Emit 'const' variables by Ben Clayton <bclayton@google.com>
  - f90313396b63da549e8c535aaa577d9fe1dea5a1 tint/reader/wgsl: Drop const_expr parsing by Ben Clayton <bclayton@google.com>
  - e48ef8ef9070c319c0a3beb372076948c2131437 tint/reader: Enable 'const' parsing for unit-tests by Ben Clayton <bclayton@google.com>
  - 511529f082daa0ae7f803024dfbce1ba1e0e9fe3 tint/resolver: Clean up workgroup-size error diagnostic by Ben Clayton <bclayton@google.com>
  - 410a3bd19a06889017740633b5cf421d70ccf664 tint/resolver: Propagate constant values between 'const's by Ben Clayton <bclayton@google.com>
  - e3834c47604ebccf4337604612eec1f51411a7bb tint/resolver: Resolve 'const' variables by Ben Clayton <bclayton@google.com>
  - 01208e7e42ae6c56b4fdb2618ff72e91708a2206 tint: Rename Global() -> GlobalVar() by Ben Clayton <bclayton@google.com>
  - 41486e11350217e4e50480411305889384beff4f tint: Rename GlobalConst() -> GlobalLet() by Ben Clayton <bclayton@google.com>
  - b4ff8c859a26e8ac060062198cfaf35d6b8c4520 tint/resolver: Simplify array size evaluation by Ben Clayton <bclayton@google.com>
  - 268d7b83571ae65b7e59a6df251ecb6ac0f5a806 tint: Add support for atomic ops to spirv reader by Antonio Maiorano <amaiorano@google.com>
  - ebed939759cb7b6fc9725e9430093166d7e7c21b tint/resolver: Tidy up variable_test.cc by Ben Clayton <bclayton@google.com>
  - 68ae36e43d1e7b6d8138bb1a794b2d3614a31f6e tint/resolver: Rename var_let_*test.cc -> variable_*test.cc by Ben Clayton <bclayton@google.com>
  - dfeaf29055e17d1a01caa7c69af50e577808cfda tint: Add sem::IndexAccessorExpression by Antonio Maiorano <amaiorano@google.com>
  - 32cb9cf2f8ae5937c3ad45de39cb0638309502fd tint/writer: Disable constant inlining for lets by Ben Clayton <bclayton@google.com>
  - c5f7e8f0bc9983a9010c5353932601b40bd45ae7 tint: Fix emitting identity matrix ctor in HLSL by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - f47887d2073504bf13152ff5ae142a6653ba322c tint/writer/msl: Generate an array<T,N> helper by Ben Clayton <bclayton@google.com>
  - 3c054304a8fc579ea085d44af010e470cb591921 tint/sem: Support arrays for sem::Constant by Ben Clayton <bclayton@google.com>
  - e8d47d52636e9499915422a53ccde978b24feb02 tint/sem: Add more checks to constant_test.cc by Ben Clayton <bclayton@google.com>
  - ab70ff7a2fc355defd4558aeb75914e10f5f3610 tint/writer/spirv: Clean up accessor tests by Ben Clayton <bclayton@google.com>
  - 3a68ab4a1718e6eb23d21d2eb4d0db14ecdf4b6d tint/writer: Minor generator cleanup. by Ben Clayton <bclayton@google.com>
  - 50414807150bdac9f41817fea7bdf939425768be tint/writer: Clean up EmitConstant() methods by Ben Clayton <bclayton@google.com>
  - 18b966321bca9ed2a7787c0adbbb18d013fac43e tint: remove duplicated ending newline in ctor_conv_intri... by Zhaoming Jiang <zhaoming.jiang@intel.com>
  - c1cb9dc1a5c038ad2d3bad0edc852688f638d9ac tint/resolver: Evaluate constant index accessors by Ben Clayton <bclayton@google.com>
  - f99671b830811d33dc1d1e234c4b66300ed007a7 tint: spir-v reader: fix atomicCompareExchangeWeak with v... by Antonio Maiorano <amaiorano@google.com>
  - 77bf233cef236a094ef845d8f1a31073c2ddd7b9 Move tint unittest behind a build flag. by dan sinclair <dsinclair@chromium.org>
  - 1ed376be34219ad52892fcb3b83787cfc026e9b4 tint/sem: Add range overload of Constant::AllZero() by Ben Clayton <bclayton@google.com>
  - ef62b58cf96ea4d0f0f22fac1d0c768e15aab00c Skip Gamma and Gamut conversions for BT.709->SRGB by jchen10 <jie.a.chen@intel.com>
  - 797f0f82e0521a4aaaad3004dc281df6581c693e tint/sem: Return vector for Type::ElementOf(matrix) by Ben Clayton <bclayton@google.com>
  - 44eb10814e2aa8b07c96573bd268cf2ea15d97b7 tint/resolver: Change return type of EvaluateConstantValu... by Ben Clayton <bclayton@google.com>
  - e021566617d6c705387c4faf4f95f1501995845a tint/reader/wgsl: Lex 'const' by Ben Clayton <bclayton@google.com>
  - 3b3ef3624120551e2599c6a02bee1e8de3b01b0f tint/sem: Add Type::DeepestElementOf() by Ben Clayton <bclayton@google.com>
  - 7ee324551c65bff676039448b1440ff6646ef9c0 tint/ast: Add 'const' AST node by Ben Clayton <bclayton@google.com>
  - 42fdeb2c8cf26a18c2bf7687cbe492a8ae46e3e5 Remove test/tint/BUILD.gn by dan sinclair <dsinclair@chromium.org>
  - 22d8dea091662c05e0ec510e0d67995cd2153618 tint/resolver: Fix ICE when failing to materialize by Ben Clayton <bclayton@google.com>
  - e4e4854b77c3bce27996d86cbcfbed0b158397df tint/resolver: Clean up 'let' / 'override' resolving by Ben Clayton <bclayton@google.com>
  - ee49b1ed9550b8a8a0018ea51d76613b2dd5a5e4 tint/resolver: Clean up 'var' resolving by Ben Clayton <bclayton@google.com>
  - 80fdd624a0919ed5578a3e8c1c0b18b3d347e1b4 tint: Process functions in dependency order by James Price <jrprice@google.com>
  - 46583621c0b17e72da334b2471898104e101406c tint: Refactor ModuleScopeVarToEntryPointParam by James Price <jrprice@google.com>
GitOrigin-RevId: c0d51f1cd8945d035ed9ff83bd37d89cbf7e3de2
Change-Id: I772e94e59da83e6466a1694a708186bdc03119ff
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/95260
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index f73c1da..09d87d7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -17,8 +17,8 @@
   testonly = true
   deps = [
     "src/tint:libtint",
+    "src/tint:tint_unittests",
     "src/tint/cmd:tint",
     "src/tint/fuzzers",
-    "test/tint:tint_unittests",
   ]
 }
diff --git a/build_overrides/tint.gni b/build_overrides/tint.gni
index fdcc866..a4594f2 100644
--- a/build_overrides/tint.gni
+++ b/build_overrides/tint.gni
@@ -13,3 +13,4 @@
 # limitations under the License.
 
 # This file contains Tint-related overrides.
+tint_build_unittests = true
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 4f17ae2..fb51327 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -13,9 +13,13 @@
 # limitations under the License.
 
 import("//build_overrides/build.gni")
-import("//testing/test.gni")
+
 import("../../tint_overrides_with_defaults.gni")
 
+if (tint_build_unittests) {
+  import("//testing/test.gni")
+}
+
 ###############################################################################
 # Common - Configs, etc. shared across targets
 ###############################################################################
@@ -213,6 +217,8 @@
     "ast/case_statement.h",
     "ast/compound_assignment_statement.cc",
     "ast/compound_assignment_statement.h",
+    "ast/const.cc",
+    "ast/const.h",
     "ast/continue_statement.cc",
     "ast/continue_statement.h",
     "ast/depth_multisampled_texture.cc",
@@ -422,6 +428,7 @@
     "sem/for_loop_statement.h",
     "sem/i32.h",
     "sem/if_statement.h",
+    "sem/index_accessor_expression.h",
     "sem/info.h",
     "sem/loop_statement.h",
     "sem/materialize.h",
@@ -501,8 +508,8 @@
     "transform/multiplanar_external_texture.h",
     "transform/num_workgroups_from_uniform.cc",
     "transform/num_workgroups_from_uniform.h",
-    "transform/promote_initializers_to_const_var.cc",
-    "transform/promote_initializers_to_const_var.h",
+    "transform/promote_initializers_to_let.cc",
+    "transform/promote_initializers_to_let.h",
     "transform/promote_side_effects_to_decl.cc",
     "transform/promote_side_effects_to_decl.h",
     "transform/remove_continue_in_switch.cc",
@@ -519,6 +526,8 @@
     "transform/simplify_pointers.h",
     "transform/single_entry_point.cc",
     "transform/single_entry_point.h",
+    "transform/spirv_atomic.cc",
+    "transform/spirv_atomic.h",
     "transform/transform.cc",
     "transform/transform.h",
     "transform/unshadow.cc",
@@ -537,8 +546,6 @@
     "transform/vertex_pulling.h",
     "transform/while_to_loop.cc",
     "transform/while_to_loop.h",
-    "transform/wrap_arrays_in_structs.cc",
-    "transform/wrap_arrays_in_structs.h",
     "transform/zero_init_workgroup_memory.cc",
     "transform/zero_init_workgroup_memory.h",
     "utils/bitcast.h",
@@ -630,6 +637,8 @@
     "sem/i32.h",
     "sem/if_statement.cc",
     "sem/if_statement.h",
+    "sem/index_accessor_expression.cc",
+    "sem/index_accessor_expression.h",
     "sem/info.cc",
     "sem/info.h",
     "sem/loop_statement.cc",
@@ -847,820 +856,820 @@
   }
 }
 
-###############################################################################
-# Gtest Gmock - Handle building inside and outside of Chromium.
-###############################################################################
-# When building outside of Chromium we need to define our own targets for GTest
-# and GMock. However when compiling inside of Chromium we need to reuse the
-# existing targets, both because Chromium has a special harness for swarming
-# and because otherwise the "gn check" fails.
+if (tint_build_unittests) {
+  ###############################################################################
+  # Gtest Gmock - Handle building inside and outside of Chromium.
+  ###############################################################################
+  # When building outside of Chromium we need to define our own targets for GTest
+  # and GMock. However when compiling inside of Chromium we need to reuse the
+  # existing targets, both because Chromium has a special harness for swarming
+  # and because otherwise the "gn check" fails.
 
-if (!build_with_chromium) {
-  # When we aren't in Chromium we define out own targets based on the location
-  # of the googletest repo.
-  config("gtest_config") {
-    include_dirs = [
-      "${tint_googletest_dir}/googletest",
-      "${tint_googletest_dir}/googletest/include",
-    ]
+  if (!build_with_chromium) {
+    # When we aren't in Chromium we define out own targets based on the location
+    # of the googletest repo.
+    config("gtest_config") {
+      include_dirs = [
+        "${tint_googletest_dir}/googletest",
+        "${tint_googletest_dir}/googletest/include",
+      ]
+    }
+    static_library("gtest") {
+      testonly = true
+      sources = [ "${tint_googletest_dir}/googletest/src/gtest-all.cc" ]
+      public_configs = [ ":gtest_config" ]
+    }
+
+    config("gmock_config") {
+      include_dirs = [
+        "${tint_googletest_dir}/googlemock",
+        "${tint_googletest_dir}/googlemock/include",
+        "${tint_googletest_dir}/googletest/include",
+      ]
+    }
+
+    static_library("gmock") {
+      testonly = true
+      sources = [ "${tint_googletest_dir}/googlemock/src/gmock-all.cc" ]
+      public_configs = [ ":gmock_config" ]
+    }
+
+    group("gmock_and_gtest") {
+      testonly = true
+      public_deps = [
+        ":gmock",
+        ":gtest",
+      ]
+    }
+  } else {
+    # When we are in Chromium we reuse its targets, and also add some deps that
+    # are needed to launch the test in swarming mode.
+    group("gmock_and_gtest") {
+      testonly = true
+      public_deps = [
+        "//base",
+        "//base/test:test_support",
+        "//testing/gmock",
+        "//testing/gtest",
+        "//third_party/googletest:gmock",
+      ]
+    }
   }
-  static_library("gtest") {
+
+  ###############################################################################
+  # Wrapping of Chromium targets
+  ###############################################################################
+  # These targets are separated because they are Chromium sources files that
+  # can't use the tint_internal config, otherwise Tint's warning flags get
+  # applied while compiling a bunch of Chromium's //base (via header inclusion)
+  source_set("tint_unittests_main") {
     testonly = true
-    sources = [ "${tint_googletest_dir}/googletest/src/gtest-all.cc" ]
-    public_configs = [ ":gtest_config" ]
+    deps = [ ":gmock_and_gtest" ]
+    if (build_with_chromium) {
+      sources = [ "//gpu/tint_unittests_main.cc" ]
+    } else {
+      sources = [ "test_main.cc" ]
+      configs += [ ":tint_unittests_config" ]
+      deps += [
+        ":libtint",
+        ":tint_unittests_hlsl_writer_src",
+        ":tint_unittests_msl_writer_src",
+        ":tint_unittests_spv_reader_src",
+      ]
+    }
   }
 
-  config("gmock_config") {
+  ###############################################################################
+  # Tests - For libtint core and optional modules
+  ###############################################################################
+  config("tint_unittests_config") {
     include_dirs = [
-      "${tint_googletest_dir}/googlemock",
       "${tint_googletest_dir}/googlemock/include",
       "${tint_googletest_dir}/googletest/include",
     ]
-  }
 
-  static_library("gmock") {
-    testonly = true
-    sources = [ "${tint_googletest_dir}/googlemock/src/gmock-all.cc" ]
-    public_configs = [ ":gmock_config" ]
-  }
-
-  group("gmock_and_gtest") {
-    testonly = true
-    public_deps = [
-      ":gmock",
-      ":gtest",
+    configs = [
+      ":tint_common_config",
+      ":tint_public_config",
     ]
   }
-} else {
-  # When we are in Chromium we reuse its targets, and also add some deps that
-  # are needed to launch the test in swarming mode.
-  group("gmock_and_gtest") {
-    testonly = true
-    public_deps = [
-      "//base",
-      "//base/test:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//third_party/googletest:gmock",
-    ]
-  }
-}
 
-###############################################################################
-# Wrapping of Chromium targets
-###############################################################################
-# These targets are separated because they are Chromium sources files that
-# can't use the tint_internal config, otherwise Tint's warning flags get
-# applied while compiling a bunch of Chromium's //base (via header inclusion)
-source_set("tint_unittests_main") {
-  testonly = true
-  deps = [ ":gmock_and_gtest" ]
-  if (build_with_chromium) {
-    sources = [ "//gpu/tint_unittests_main.cc" ]
-  } else {
-    sources = [ "test_main.cc" ]
-    configs += [ ":tint_unittests_config" ]
-    deps += [
-      ":libtint",
-      ":tint_unittests_hlsl_writer_src",
-      ":tint_unittests_msl_writer_src",
-      ":tint_unittests_spv_reader_src",
-    ]
-  }
-}
+  template("tint_unittests_source_set") {
+    source_set(target_name) {
+      forward_variables_from(invoker, "*", [ "configs" ])
 
-###############################################################################
-# Tests - For libtint core and optional modules
-###############################################################################
-config("tint_unittests_config") {
-  include_dirs = [
-    "${tint_googletest_dir}/googlemock/include",
-    "${tint_googletest_dir}/googletest/include",
-  ]
+      if (defined(invoker.configs)) {
+        configs += invoker.configs
+      }
+      configs += [ ":tint_unittests_config" ]
+      if (build_with_chromium) {
+        configs -= [ "//build/config/compiler:chromium_code" ]
+        configs += [ "//build/config/compiler:no_chromium_code" ]
+      }
 
-  configs = [
-    ":tint_common_config",
-    ":tint_public_config",
-  ]
-}
+      testonly = true
 
-template("tint_unittests_source_set") {
-  source_set(target_name) {
-    forward_variables_from(invoker, "*", [ "configs" ])
-
-    if (defined(invoker.configs)) {
-      configs += invoker.configs
+      if (!defined(invoker.deps)) {
+        deps = []
+      }
+      deps += [
+        ":gmock_and_gtest",
+        ":libtint",
+        ":tint_utils_io",
+      ]
     }
+  }
+
+  tint_unittests_source_set("tint_unittests_ast_src") {
+    sources = [
+      "ast/alias_test.cc",
+      "ast/array_test.cc",
+      "ast/assignment_statement_test.cc",
+      "ast/atomic_test.cc",
+      "ast/binary_expression_test.cc",
+      "ast/binding_attribute_test.cc",
+      "ast/bitcast_expression_test.cc",
+      "ast/block_statement_test.cc",
+      "ast/bool_literal_expression_test.cc",
+      "ast/bool_test.cc",
+      "ast/break_statement_test.cc",
+      "ast/builtin_attribute_test.cc",
+      "ast/builtin_texture_helper_test.cc",
+      "ast/builtin_texture_helper_test.h",
+      "ast/call_expression_test.cc",
+      "ast/call_statement_test.cc",
+      "ast/case_statement_test.cc",
+      "ast/compound_assignment_statement_test.cc",
+      "ast/continue_statement_test.cc",
+      "ast/depth_multisampled_texture_test.cc",
+      "ast/depth_texture_test.cc",
+      "ast/discard_statement_test.cc",
+      "ast/enable_test.cc",
+      "ast/extension_test.cc",
+      "ast/external_texture_test.cc",
+      "ast/f16_test.cc",
+      "ast/f32_test.cc",
+      "ast/fallthrough_statement_test.cc",
+      "ast/float_literal_expression_test.cc",
+      "ast/for_loop_statement_test.cc",
+      "ast/function_test.cc",
+      "ast/group_attribute_test.cc",
+      "ast/i32_test.cc",
+      "ast/id_attribute_test.cc",
+      "ast/identifier_expression_test.cc",
+      "ast/if_statement_test.cc",
+      "ast/increment_decrement_statement_test.cc",
+      "ast/index_accessor_expression_test.cc",
+      "ast/int_literal_expression_test.cc",
+      "ast/interpolate_attribute_test.cc",
+      "ast/invariant_attribute_test.cc",
+      "ast/location_attribute_test.cc",
+      "ast/loop_statement_test.cc",
+      "ast/matrix_test.cc",
+      "ast/member_accessor_expression_test.cc",
+      "ast/module_clone_test.cc",
+      "ast/module_test.cc",
+      "ast/multisampled_texture_test.cc",
+      "ast/phony_expression_test.cc",
+      "ast/pointer_test.cc",
+      "ast/return_statement_test.cc",
+      "ast/sampled_texture_test.cc",
+      "ast/sampler_test.cc",
+      "ast/stage_attribute_test.cc",
+      "ast/storage_texture_test.cc",
+      "ast/stride_attribute_test.cc",
+      "ast/struct_member_align_attribute_test.cc",
+      "ast/struct_member_offset_attribute_test.cc",
+      "ast/struct_member_size_attribute_test.cc",
+      "ast/struct_member_test.cc",
+      "ast/struct_test.cc",
+      "ast/switch_statement_test.cc",
+      "ast/test_helper.h",
+      "ast/texture_test.cc",
+      "ast/traverse_expressions_test.cc",
+      "ast/u32_test.cc",
+      "ast/unary_op_expression_test.cc",
+      "ast/variable_decl_statement_test.cc",
+      "ast/variable_test.cc",
+      "ast/vector_test.cc",
+      "ast/while_statement_test.cc",
+      "ast/workgroup_attribute_test.cc",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_diagnostic_src") {
+    sources = [
+      "diagnostic/diagnostic_test.cc",
+      "diagnostic/formatter_test.cc",
+      "diagnostic/printer_test.cc",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_inspector_src") {
+    sources = [
+      "inspector/inspector_test.cc",
+      "inspector/test_inspector_builder.cc",
+      "inspector/test_inspector_builder.h",
+      "inspector/test_inspector_runner.cc",
+      "inspector/test_inspector_runner.h",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_resolver_src") {
+    sources = [
+      "resolver/array_accessor_test.cc",
+      "resolver/assignment_validation_test.cc",
+      "resolver/atomics_test.cc",
+      "resolver/atomics_validation_test.cc",
+      "resolver/attribute_validation_test.cc",
+      "resolver/bitcast_validation_test.cc",
+      "resolver/builtin_test.cc",
+      "resolver/builtin_validation_test.cc",
+      "resolver/builtins_validation_test.cc",
+      "resolver/call_test.cc",
+      "resolver/call_validation_test.cc",
+      "resolver/compound_assignment_validation_test.cc",
+      "resolver/compound_statement_test.cc",
+      "resolver/control_block_validation_test.cc",
+      "resolver/dependency_graph_test.cc",
+      "resolver/entry_point_validation_test.cc",
+      "resolver/function_validation_test.cc",
+      "resolver/host_shareable_validation_test.cc",
+      "resolver/increment_decrement_validation_test.cc",
+      "resolver/intrinsic_table_test.cc",
+      "resolver/is_host_shareable_test.cc",
+      "resolver/is_storeable_test.cc",
+      "resolver/materialize_test.cc",
+      "resolver/pipeline_overridable_constant_test.cc",
+      "resolver/ptr_ref_test.cc",
+      "resolver/ptr_ref_validation_test.cc",
+      "resolver/resolver_behavior_test.cc",
+      "resolver/resolver_constants_test.cc",
+      "resolver/resolver_test.cc",
+      "resolver/resolver_test_helper.cc",
+      "resolver/resolver_test_helper.h",
+      "resolver/side_effects_test.cc",
+      "resolver/source_variable_test.cc",
+      "resolver/storage_class_layout_validation_test.cc",
+      "resolver/storage_class_validation_test.cc",
+      "resolver/struct_layout_test.cc",
+      "resolver/struct_pipeline_stage_use_test.cc",
+      "resolver/struct_storage_class_use_test.cc",
+      "resolver/type_constructor_validation_test.cc",
+      "resolver/type_validation_test.cc",
+      "resolver/uniformity_test.cc",
+      "resolver/validation_test.cc",
+      "resolver/validator_is_storeable_test.cc",
+      "resolver/variable_test.cc",
+      "resolver/variable_validation_test.cc",
+    ]
+    deps = [ ":tint_unittests_ast_src" ]
+  }
+
+  tint_unittests_source_set("tint_unittests_sem_src") {
+    sources = [
+      "sem/atomic_test.cc",
+      "sem/bool_test.cc",
+      "sem/builtin_test.cc",
+      "sem/depth_multisampled_texture_test.cc",
+      "sem/depth_texture_test.cc",
+      "sem/expression_test.cc",
+      "sem/external_texture_test.cc",
+      "sem/f16_test.cc",
+      "sem/f32_test.cc",
+      "sem/i32_test.cc",
+      "sem/matrix_test.cc",
+      "sem/multisampled_texture_test.cc",
+      "sem/pointer_test.cc",
+      "sem/reference_test.cc",
+      "sem/sampled_texture_test.cc",
+      "sem/sampler_test.cc",
+      "sem/sem_array_test.cc",
+      "sem/sem_struct_test.cc",
+      "sem/storage_texture_test.cc",
+      "sem/texture_test.cc",
+      "sem/type_manager_test.cc",
+      "sem/type_test.cc",
+      "sem/u32_test.cc",
+      "sem/vector_test.cc",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_text_src") {
+    sources = [ "text/unicode_test.cc" ]
+  }
+
+  tint_unittests_source_set("tint_unittests_transform_src") {
+    sources = [
+      "transform/add_empty_entry_point_test.cc",
+      "transform/add_spirv_block_attribute_test.cc",
+      "transform/array_length_from_uniform_test.cc",
+      "transform/binding_remapper_test.cc",
+      "transform/builtin_polyfill_test.cc",
+      "transform/calculate_array_length_test.cc",
+      "transform/canonicalize_entry_point_io_test.cc",
+      "transform/combine_samplers_test.cc",
+      "transform/decompose_memory_access_test.cc",
+      "transform/decompose_strided_array_test.cc",
+      "transform/decompose_strided_matrix_test.cc",
+      "transform/disable_uniformity_analysis_test.cc",
+      "transform/expand_compound_assignment_test.cc",
+      "transform/first_index_offset_test.cc",
+      "transform/fold_trivial_single_use_lets_test.cc",
+      "transform/for_loop_to_loop_test.cc",
+      "transform/localize_struct_array_assignment_test.cc",
+      "transform/loop_to_for_loop_test.cc",
+      "transform/module_scope_var_to_entry_point_param_test.cc",
+      "transform/multiplanar_external_texture_test.cc",
+      "transform/num_workgroups_from_uniform_test.cc",
+      "transform/promote_initializers_to_let_test.cc",
+      "transform/promote_side_effects_to_decl_test.cc",
+      "transform/remove_continue_in_switch_test.cc",
+      "transform/remove_phonies_test.cc",
+      "transform/remove_unreachable_statements_test.cc",
+      "transform/renamer_test.cc",
+      "transform/robustness_test.cc",
+      "transform/simplify_pointers_test.cc",
+      "transform/single_entry_point_test.cc",
+      "transform/spirv_atomic_test.cc",
+      "transform/test_helper.h",
+      "transform/transform_test.cc",
+      "transform/unshadow_test.cc",
+      "transform/unwind_discard_functions_test.cc",
+      "transform/utils/get_insertion_point_test.cc",
+      "transform/utils/hoist_to_decl_before_test.cc",
+      "transform/var_for_dynamic_index_test.cc",
+      "transform/vectorize_scalar_matrix_constructors_test.cc",
+      "transform/vertex_pulling_test.cc",
+      "transform/while_to_loop_test.cc",
+      "transform/zero_init_workgroup_memory_test.cc",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_utils_src") {
+    sources = [
+      "utils/bitcast_test.cc",
+      "utils/crc32_test.cc",
+      "utils/defer_test.cc",
+      "utils/enum_set_test.cc",
+      "utils/hash_test.cc",
+      "utils/io/command_test.cc",
+      "utils/io/tmpfile_test.cc",
+      "utils/map_test.cc",
+      "utils/math_test.cc",
+      "utils/result_test.cc",
+      "utils/reverse_test.cc",
+      "utils/scoped_assignment_test.cc",
+      "utils/string_test.cc",
+      "utils/transform_test.cc",
+      "utils/unique_allocator_test.cc",
+      "utils/unique_vector_test.cc",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_writer_src") {
+    sources = [
+      "writer/append_vector_test.cc",
+      "writer/flatten_bindings_test.cc",
+      "writer/float_to_string_test.cc",
+      "writer/generate_external_texture_bindings_test.cc",
+      "writer/text_generator_test.cc",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_spv_reader_src") {
+    sources = [
+      "reader/spirv/enum_converter_test.cc",
+      "reader/spirv/fail_stream_test.cc",
+      "reader/spirv/function_arithmetic_test.cc",
+      "reader/spirv/function_bit_test.cc",
+      "reader/spirv/function_call_test.cc",
+      "reader/spirv/function_cfg_test.cc",
+      "reader/spirv/function_composite_test.cc",
+      "reader/spirv/function_conversion_test.cc",
+      "reader/spirv/function_decl_test.cc",
+      "reader/spirv/function_glsl_std_450_test.cc",
+      "reader/spirv/function_logical_test.cc",
+      "reader/spirv/function_memory_test.cc",
+      "reader/spirv/function_misc_test.cc",
+      "reader/spirv/function_var_test.cc",
+      "reader/spirv/namer_test.cc",
+      "reader/spirv/parser_impl_barrier_test.cc",
+      "reader/spirv/parser_impl_convert_member_decoration_test.cc",
+      "reader/spirv/parser_impl_convert_type_test.cc",
+      "reader/spirv/parser_impl_function_decl_test.cc",
+      "reader/spirv/parser_impl_get_decorations_test.cc",
+      "reader/spirv/parser_impl_handle_test.cc",
+      "reader/spirv/parser_impl_import_test.cc",
+      "reader/spirv/parser_impl_module_var_test.cc",
+      "reader/spirv/parser_impl_named_types_test.cc",
+      "reader/spirv/parser_impl_test.cc",
+      "reader/spirv/parser_impl_test_helper.cc",
+      "reader/spirv/parser_impl_test_helper.h",
+      "reader/spirv/parser_impl_user_name_test.cc",
+      "reader/spirv/parser_test.cc",
+      "reader/spirv/parser_type_test.cc",
+      "reader/spirv/spirv_tools_helpers_test.cc",
+      "reader/spirv/spirv_tools_helpers_test.h",
+      "reader/spirv/usage_test.cc",
+    ]
+
+    deps = [ ":libtint_spv_reader_src" ]
+  }
+
+  tint_unittests_source_set("tint_unittests_spv_writer_src") {
+    sources = [
+      "writer/spirv/binary_writer_test.cc",
+      "writer/spirv/builder_accessor_expression_test.cc",
+      "writer/spirv/builder_assign_test.cc",
+      "writer/spirv/builder_binary_expression_test.cc",
+      "writer/spirv/builder_bitcast_expression_test.cc",
+      "writer/spirv/builder_block_test.cc",
+      "writer/spirv/builder_builtin_test.cc",
+      "writer/spirv/builder_builtin_texture_test.cc",
+      "writer/spirv/builder_call_test.cc",
+      "writer/spirv/builder_constructor_expression_test.cc",
+      "writer/spirv/builder_discard_test.cc",
+      "writer/spirv/builder_entry_point_test.cc",
+      "writer/spirv/builder_format_conversion_test.cc",
+      "writer/spirv/builder_function_attribute_test.cc",
+      "writer/spirv/builder_function_test.cc",
+      "writer/spirv/builder_function_variable_test.cc",
+      "writer/spirv/builder_global_variable_test.cc",
+      "writer/spirv/builder_ident_expression_test.cc",
+      "writer/spirv/builder_if_test.cc",
+      "writer/spirv/builder_literal_test.cc",
+      "writer/spirv/builder_loop_test.cc",
+      "writer/spirv/builder_return_test.cc",
+      "writer/spirv/builder_switch_test.cc",
+      "writer/spirv/builder_test.cc",
+      "writer/spirv/builder_type_test.cc",
+      "writer/spirv/builder_unary_op_expression_test.cc",
+      "writer/spirv/instruction_test.cc",
+      "writer/spirv/operand_test.cc",
+      "writer/spirv/scalar_constant_test.cc",
+      "writer/spirv/spv_dump.cc",
+      "writer/spirv/spv_dump.h",
+      "writer/spirv/test_helper.h",
+    ]
+
+    deps = [
+      ":libtint_spv_writer_src",
+      ":tint_unittests_ast_src",
+      "${tint_spirv_tools_dir}/:spvtools",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_wgsl_reader_src") {
+    sources = [
+      "reader/wgsl/lexer_test.cc",
+      "reader/wgsl/parser_impl_additive_expression_test.cc",
+      "reader/wgsl/parser_impl_and_expression_test.cc",
+      "reader/wgsl/parser_impl_argument_expression_list_test.cc",
+      "reader/wgsl/parser_impl_assignment_stmt_test.cc",
+      "reader/wgsl/parser_impl_body_stmt_test.cc",
+      "reader/wgsl/parser_impl_break_stmt_test.cc",
+      "reader/wgsl/parser_impl_bug_cases_test.cc",
+      "reader/wgsl/parser_impl_call_stmt_test.cc",
+      "reader/wgsl/parser_impl_case_body_test.cc",
+      "reader/wgsl/parser_impl_const_literal_test.cc",
+      "reader/wgsl/parser_impl_continue_stmt_test.cc",
+      "reader/wgsl/parser_impl_continuing_stmt_test.cc",
+      "reader/wgsl/parser_impl_depth_texture_test.cc",
+      "reader/wgsl/parser_impl_enable_directive_test.cc",
+      "reader/wgsl/parser_impl_equality_expression_test.cc",
+      "reader/wgsl/parser_impl_error_msg_test.cc",
+      "reader/wgsl/parser_impl_error_resync_test.cc",
+      "reader/wgsl/parser_impl_exclusive_or_expression_test.cc",
+      "reader/wgsl/parser_impl_external_texture_test.cc",
+      "reader/wgsl/parser_impl_for_stmt_test.cc",
+      "reader/wgsl/parser_impl_function_attribute_list_test.cc",
+      "reader/wgsl/parser_impl_function_attribute_test.cc",
+      "reader/wgsl/parser_impl_function_decl_test.cc",
+      "reader/wgsl/parser_impl_function_header_test.cc",
+      "reader/wgsl/parser_impl_global_constant_decl_test.cc",
+      "reader/wgsl/parser_impl_global_decl_test.cc",
+      "reader/wgsl/parser_impl_global_variable_decl_test.cc",
+      "reader/wgsl/parser_impl_if_stmt_test.cc",
+      "reader/wgsl/parser_impl_inclusive_or_expression_test.cc",
+      "reader/wgsl/parser_impl_increment_decrement_stmt_test.cc",
+      "reader/wgsl/parser_impl_logical_and_expression_test.cc",
+      "reader/wgsl/parser_impl_logical_or_expression_test.cc",
+      "reader/wgsl/parser_impl_loop_stmt_test.cc",
+      "reader/wgsl/parser_impl_multiplicative_expression_test.cc",
+      "reader/wgsl/parser_impl_param_list_test.cc",
+      "reader/wgsl/parser_impl_paren_rhs_stmt_test.cc",
+      "reader/wgsl/parser_impl_pipeline_stage_test.cc",
+      "reader/wgsl/parser_impl_primary_expression_test.cc",
+      "reader/wgsl/parser_impl_relational_expression_test.cc",
+      "reader/wgsl/parser_impl_reserved_keyword_test.cc",
+      "reader/wgsl/parser_impl_sampled_texture_test.cc",
+      "reader/wgsl/parser_impl_sampler_test.cc",
+      "reader/wgsl/parser_impl_shift_expression_test.cc",
+      "reader/wgsl/parser_impl_singular_expression_test.cc",
+      "reader/wgsl/parser_impl_statement_test.cc",
+      "reader/wgsl/parser_impl_statements_test.cc",
+      "reader/wgsl/parser_impl_storage_class_test.cc",
+      "reader/wgsl/parser_impl_storage_texture_test.cc",
+      "reader/wgsl/parser_impl_struct_attribute_decl_test.cc",
+      "reader/wgsl/parser_impl_struct_body_decl_test.cc",
+      "reader/wgsl/parser_impl_struct_decl_test.cc",
+      "reader/wgsl/parser_impl_struct_member_attribute_decl_test.cc",
+      "reader/wgsl/parser_impl_struct_member_attribute_test.cc",
+      "reader/wgsl/parser_impl_struct_member_test.cc",
+      "reader/wgsl/parser_impl_switch_body_test.cc",
+      "reader/wgsl/parser_impl_switch_stmt_test.cc",
+      "reader/wgsl/parser_impl_test.cc",
+      "reader/wgsl/parser_impl_test_helper.cc",
+      "reader/wgsl/parser_impl_test_helper.h",
+      "reader/wgsl/parser_impl_texel_format_test.cc",
+      "reader/wgsl/parser_impl_texture_sampler_test.cc",
+      "reader/wgsl/parser_impl_type_alias_test.cc",
+      "reader/wgsl/parser_impl_type_decl_test.cc",
+      "reader/wgsl/parser_impl_unary_expression_test.cc",
+      "reader/wgsl/parser_impl_variable_attribute_list_test.cc",
+      "reader/wgsl/parser_impl_variable_attribute_test.cc",
+      "reader/wgsl/parser_impl_variable_decl_test.cc",
+      "reader/wgsl/parser_impl_variable_ident_decl_test.cc",
+      "reader/wgsl/parser_impl_variable_qualifier_test.cc",
+      "reader/wgsl/parser_impl_variable_stmt_test.cc",
+      "reader/wgsl/parser_impl_while_stmt_test.cc",
+      "reader/wgsl/parser_test.cc",
+      "reader/wgsl/token_test.cc",
+    ]
+
+    deps = [ ":libtint_wgsl_reader_src" ]
+  }
+
+  tint_unittests_source_set("tint_unittests_wgsl_writer_src") {
+    sources = [
+      "writer/wgsl/generator_impl_alias_type_test.cc",
+      "writer/wgsl/generator_impl_array_accessor_test.cc",
+      "writer/wgsl/generator_impl_assign_test.cc",
+      "writer/wgsl/generator_impl_binary_test.cc",
+      "writer/wgsl/generator_impl_bitcast_test.cc",
+      "writer/wgsl/generator_impl_block_test.cc",
+      "writer/wgsl/generator_impl_break_test.cc",
+      "writer/wgsl/generator_impl_call_test.cc",
+      "writer/wgsl/generator_impl_case_test.cc",
+      "writer/wgsl/generator_impl_cast_test.cc",
+      "writer/wgsl/generator_impl_constructor_test.cc",
+      "writer/wgsl/generator_impl_continue_test.cc",
+      "writer/wgsl/generator_impl_discard_test.cc",
+      "writer/wgsl/generator_impl_enable_test.cc",
+      "writer/wgsl/generator_impl_fallthrough_test.cc",
+      "writer/wgsl/generator_impl_function_test.cc",
+      "writer/wgsl/generator_impl_global_decl_test.cc",
+      "writer/wgsl/generator_impl_identifier_test.cc",
+      "writer/wgsl/generator_impl_if_test.cc",
+      "writer/wgsl/generator_impl_literal_test.cc",
+      "writer/wgsl/generator_impl_loop_test.cc",
+      "writer/wgsl/generator_impl_member_accessor_test.cc",
+      "writer/wgsl/generator_impl_return_test.cc",
+      "writer/wgsl/generator_impl_switch_test.cc",
+      "writer/wgsl/generator_impl_test.cc",
+      "writer/wgsl/generator_impl_type_test.cc",
+      "writer/wgsl/generator_impl_unary_op_test.cc",
+      "writer/wgsl/generator_impl_variable_decl_statement_test.cc",
+      "writer/wgsl/generator_impl_variable_test.cc",
+      "writer/wgsl/test_helper.h",
+    ]
+
+    deps = [
+      ":libtint_wgsl_writer_src",
+      ":tint_unittests_ast_src",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_msl_writer_src") {
+    sources = [
+      "writer/msl/generator_impl_array_accessor_test.cc",
+      "writer/msl/generator_impl_assign_test.cc",
+      "writer/msl/generator_impl_binary_test.cc",
+      "writer/msl/generator_impl_bitcast_test.cc",
+      "writer/msl/generator_impl_block_test.cc",
+      "writer/msl/generator_impl_break_test.cc",
+      "writer/msl/generator_impl_builtin_test.cc",
+      "writer/msl/generator_impl_builtin_texture_test.cc",
+      "writer/msl/generator_impl_call_test.cc",
+      "writer/msl/generator_impl_case_test.cc",
+      "writer/msl/generator_impl_cast_test.cc",
+      "writer/msl/generator_impl_constructor_test.cc",
+      "writer/msl/generator_impl_continue_test.cc",
+      "writer/msl/generator_impl_discard_test.cc",
+      "writer/msl/generator_impl_function_test.cc",
+      "writer/msl/generator_impl_identifier_test.cc",
+      "writer/msl/generator_impl_if_test.cc",
+      "writer/msl/generator_impl_import_test.cc",
+      "writer/msl/generator_impl_loop_test.cc",
+      "writer/msl/generator_impl_member_accessor_test.cc",
+      "writer/msl/generator_impl_module_constant_test.cc",
+      "writer/msl/generator_impl_return_test.cc",
+      "writer/msl/generator_impl_sanitizer_test.cc",
+      "writer/msl/generator_impl_switch_test.cc",
+      "writer/msl/generator_impl_test.cc",
+      "writer/msl/generator_impl_type_test.cc",
+      "writer/msl/generator_impl_unary_op_test.cc",
+      "writer/msl/generator_impl_variable_decl_statement_test.cc",
+      "writer/msl/test_helper.h",
+    ]
+
+    deps = [
+      ":libtint_msl_writer_src",
+      ":tint_unittests_ast_src",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_hlsl_writer_src") {
+    sources = [
+      "writer/hlsl/generator_impl_array_accessor_test.cc",
+      "writer/hlsl/generator_impl_assign_test.cc",
+      "writer/hlsl/generator_impl_binary_test.cc",
+      "writer/hlsl/generator_impl_bitcast_test.cc",
+      "writer/hlsl/generator_impl_block_test.cc",
+      "writer/hlsl/generator_impl_break_test.cc",
+      "writer/hlsl/generator_impl_builtin_test.cc",
+      "writer/hlsl/generator_impl_builtin_texture_test.cc",
+      "writer/hlsl/generator_impl_call_test.cc",
+      "writer/hlsl/generator_impl_case_test.cc",
+      "writer/hlsl/generator_impl_cast_test.cc",
+      "writer/hlsl/generator_impl_constructor_test.cc",
+      "writer/hlsl/generator_impl_continue_test.cc",
+      "writer/hlsl/generator_impl_discard_test.cc",
+      "writer/hlsl/generator_impl_function_test.cc",
+      "writer/hlsl/generator_impl_identifier_test.cc",
+      "writer/hlsl/generator_impl_if_test.cc",
+      "writer/hlsl/generator_impl_import_test.cc",
+      "writer/hlsl/generator_impl_loop_test.cc",
+      "writer/hlsl/generator_impl_member_accessor_test.cc",
+      "writer/hlsl/generator_impl_module_constant_test.cc",
+      "writer/hlsl/generator_impl_return_test.cc",
+      "writer/hlsl/generator_impl_sanitizer_test.cc",
+      "writer/hlsl/generator_impl_switch_test.cc",
+      "writer/hlsl/generator_impl_test.cc",
+      "writer/hlsl/generator_impl_type_test.cc",
+      "writer/hlsl/generator_impl_unary_op_test.cc",
+      "writer/hlsl/generator_impl_variable_decl_statement_test.cc",
+      "writer/hlsl/generator_impl_workgroup_var_test.cc",
+      "writer/hlsl/test_helper.h",
+    ]
+
+    deps = [
+      ":libtint_hlsl_writer_src",
+      ":tint_unittests_ast_src",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_glsl_writer_src") {
+    sources = [
+      "writer/glsl/generator_impl_array_accessor_test.cc",
+      "writer/glsl/generator_impl_assign_test.cc",
+      "writer/glsl/generator_impl_binary_test.cc",
+      "writer/glsl/generator_impl_bitcast_test.cc",
+      "writer/glsl/generator_impl_block_test.cc",
+      "writer/glsl/generator_impl_break_test.cc",
+      "writer/glsl/generator_impl_builtin_test.cc",
+      "writer/glsl/generator_impl_builtin_texture_test.cc",
+      "writer/glsl/generator_impl_call_test.cc",
+      "writer/glsl/generator_impl_case_test.cc",
+      "writer/glsl/generator_impl_cast_test.cc",
+      "writer/glsl/generator_impl_constructor_test.cc",
+      "writer/glsl/generator_impl_continue_test.cc",
+      "writer/glsl/generator_impl_discard_test.cc",
+      "writer/glsl/generator_impl_function_test.cc",
+      "writer/glsl/generator_impl_identifier_test.cc",
+      "writer/glsl/generator_impl_if_test.cc",
+      "writer/glsl/generator_impl_import_test.cc",
+      "writer/glsl/generator_impl_loop_test.cc",
+      "writer/glsl/generator_impl_member_accessor_test.cc",
+      "writer/glsl/generator_impl_module_constant_test.cc",
+      "writer/glsl/generator_impl_return_test.cc",
+      "writer/glsl/generator_impl_sanitizer_test.cc",
+      "writer/glsl/generator_impl_storage_buffer_test.cc",
+      "writer/glsl/generator_impl_switch_test.cc",
+      "writer/glsl/generator_impl_test.cc",
+      "writer/glsl/generator_impl_type_test.cc",
+      "writer/glsl/generator_impl_unary_op_test.cc",
+      "writer/glsl/generator_impl_uniform_buffer_test.cc",
+      "writer/glsl/generator_impl_variable_decl_statement_test.cc",
+      "writer/glsl/generator_impl_workgroup_var_test.cc",
+      "writer/glsl/test_helper.h",
+    ]
+
+    deps = [
+      ":libtint_glsl_writer_src",
+      ":tint_unittests_ast_src",
+      ":tint_unittests_transform_src",
+    ]
+  }
+
+  tint_unittests_source_set("tint_unittests_core_src") {
+    sources = [
+      "castable_test.cc",
+      "clone_context_test.cc",
+      "debug_test.cc",
+      "demangler_test.cc",
+      "number_test.cc",
+      "program_builder_test.cc",
+      "program_test.cc",
+      "scope_stack_test.cc",
+      "source_test.cc",
+      "symbol_table_test.cc",
+      "symbol_test.cc",
+      "traits_test.cc",
+      "utils/block_allocator_test.cc",
+    ]
+
+    deps = [ ":tint_unittests_ast_src" ]
+  }
+
+  if (build_with_chromium) {
+    tint_unittests_source_set("tint_unittests_fuzzer_src") {
+      sources = [ "fuzzers/random_generator_test.cc" ]
+
+      deps = [
+        ":tint_unittests_core_src",
+        "fuzzers:tint_fuzzer_common_src",
+      ]
+    }
+  }
+
+  source_set("tint_unittests_src") {
+    testonly = true
+
+    deps = [
+      ":libtint_wgsl_reader_src",
+      ":libtint_wgsl_writer_src",
+      ":tint_unittests_ast_src",
+      ":tint_unittests_core_src",
+      ":tint_unittests_diagnostic_src",
+      ":tint_unittests_inspector_src",
+      ":tint_unittests_resolver_src",
+      ":tint_unittests_sem_src",
+      ":tint_unittests_text_src",
+      ":tint_unittests_transform_src",
+      ":tint_unittests_utils_src",
+      ":tint_unittests_writer_src",
+    ]
+
+    if (tint_build_spv_reader) {
+      deps += [ ":tint_unittests_spv_reader_src" ]
+    }
+
+    if (tint_build_spv_writer) {
+      deps += [ ":tint_unittests_spv_writer_src" ]
+    }
+
+    if (tint_build_wgsl_reader) {
+      deps += [ ":tint_unittests_wgsl_reader_src" ]
+    }
+
+    if (tint_build_wgsl_writer) {
+      deps += [ ":tint_unittests_wgsl_writer_src" ]
+    }
+
+    if (tint_build_msl_writer) {
+      deps += [ ":tint_unittests_msl_writer_src" ]
+    }
+
+    if (tint_build_hlsl_writer) {
+      deps += [ ":tint_unittests_hlsl_writer_src" ]
+    }
+
+    if (tint_build_glsl_writer) {
+      deps += [ ":tint_unittests_glsl_writer_src" ]
+    }
+
+    if (build_with_chromium) {
+      deps += [ ":tint_unittests_fuzzer_src" ]
+    }
+
     configs += [ ":tint_unittests_config" ]
+
+    if (build_with_chromium) {
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [ "//build/config/compiler:no_chromium_code" ]
+    }
+  }
+
+  test("tint_unittests") {
+    deps = [
+      ":gmock_and_gtest",
+      ":tint_unittests_src",
+      "${tint_spirv_tools_dir}/:spvtools",
+      "${tint_spirv_tools_dir}/:spvtools_opt",
+      "${tint_spirv_tools_dir}/:spvtools_val",
+    ]
+
+    deps += [ ":tint_unittests_main" ]
+
+    configs += [ ":tint_unittests_config" ]
+
     if (build_with_chromium) {
       configs -= [ "//build/config/compiler:chromium_code" ]
       configs += [ "//build/config/compiler:no_chromium_code" ]
     }
 
     testonly = true
-
-    if (!defined(invoker.deps)) {
-      deps = []
-    }
-    deps += [
-      ":gmock_and_gtest",
-      ":libtint",
-      ":tint_utils_io",
-    ]
   }
 }
-
-tint_unittests_source_set("tint_unittests_ast_src") {
-  sources = [
-    "ast/alias_test.cc",
-    "ast/array_test.cc",
-    "ast/assignment_statement_test.cc",
-    "ast/atomic_test.cc",
-    "ast/binary_expression_test.cc",
-    "ast/binding_attribute_test.cc",
-    "ast/bitcast_expression_test.cc",
-    "ast/block_statement_test.cc",
-    "ast/bool_literal_expression_test.cc",
-    "ast/bool_test.cc",
-    "ast/break_statement_test.cc",
-    "ast/builtin_attribute_test.cc",
-    "ast/builtin_texture_helper_test.cc",
-    "ast/builtin_texture_helper_test.h",
-    "ast/call_expression_test.cc",
-    "ast/call_statement_test.cc",
-    "ast/case_statement_test.cc",
-    "ast/compound_assignment_statement_test.cc",
-    "ast/continue_statement_test.cc",
-    "ast/depth_multisampled_texture_test.cc",
-    "ast/depth_texture_test.cc",
-    "ast/discard_statement_test.cc",
-    "ast/enable_test.cc",
-    "ast/extension_test.cc",
-    "ast/external_texture_test.cc",
-    "ast/f16_test.cc",
-    "ast/f32_test.cc",
-    "ast/fallthrough_statement_test.cc",
-    "ast/float_literal_expression_test.cc",
-    "ast/for_loop_statement_test.cc",
-    "ast/function_test.cc",
-    "ast/group_attribute_test.cc",
-    "ast/i32_test.cc",
-    "ast/id_attribute_test.cc",
-    "ast/identifier_expression_test.cc",
-    "ast/if_statement_test.cc",
-    "ast/increment_decrement_statement_test.cc",
-    "ast/index_accessor_expression_test.cc",
-    "ast/int_literal_expression_test.cc",
-    "ast/interpolate_attribute_test.cc",
-    "ast/invariant_attribute_test.cc",
-    "ast/location_attribute_test.cc",
-    "ast/loop_statement_test.cc",
-    "ast/matrix_test.cc",
-    "ast/member_accessor_expression_test.cc",
-    "ast/module_clone_test.cc",
-    "ast/module_test.cc",
-    "ast/multisampled_texture_test.cc",
-    "ast/phony_expression_test.cc",
-    "ast/pointer_test.cc",
-    "ast/return_statement_test.cc",
-    "ast/sampled_texture_test.cc",
-    "ast/sampler_test.cc",
-    "ast/stage_attribute_test.cc",
-    "ast/storage_texture_test.cc",
-    "ast/stride_attribute_test.cc",
-    "ast/struct_member_align_attribute_test.cc",
-    "ast/struct_member_offset_attribute_test.cc",
-    "ast/struct_member_size_attribute_test.cc",
-    "ast/struct_member_test.cc",
-    "ast/struct_test.cc",
-    "ast/switch_statement_test.cc",
-    "ast/test_helper.h",
-    "ast/texture_test.cc",
-    "ast/traverse_expressions_test.cc",
-    "ast/u32_test.cc",
-    "ast/unary_op_expression_test.cc",
-    "ast/variable_decl_statement_test.cc",
-    "ast/variable_test.cc",
-    "ast/vector_test.cc",
-    "ast/while_statement_test.cc",
-    "ast/workgroup_attribute_test.cc",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_diagnostic_src") {
-  sources = [
-    "diagnostic/diagnostic_test.cc",
-    "diagnostic/formatter_test.cc",
-    "diagnostic/printer_test.cc",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_inspector_src") {
-  sources = [
-    "inspector/inspector_test.cc",
-    "inspector/test_inspector_builder.cc",
-    "inspector/test_inspector_builder.h",
-    "inspector/test_inspector_runner.cc",
-    "inspector/test_inspector_runner.h",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_resolver_src") {
-  sources = [
-    "resolver/array_accessor_test.cc",
-    "resolver/assignment_validation_test.cc",
-    "resolver/atomics_test.cc",
-    "resolver/atomics_validation_test.cc",
-    "resolver/attribute_validation_test.cc",
-    "resolver/bitcast_validation_test.cc",
-    "resolver/builtin_test.cc",
-    "resolver/builtin_validation_test.cc",
-    "resolver/builtins_validation_test.cc",
-    "resolver/call_test.cc",
-    "resolver/call_validation_test.cc",
-    "resolver/compound_assignment_validation_test.cc",
-    "resolver/compound_statement_test.cc",
-    "resolver/control_block_validation_test.cc",
-    "resolver/dependency_graph_test.cc",
-    "resolver/entry_point_validation_test.cc",
-    "resolver/function_validation_test.cc",
-    "resolver/host_shareable_validation_test.cc",
-    "resolver/increment_decrement_validation_test.cc",
-    "resolver/intrinsic_table_test.cc",
-    "resolver/is_host_shareable_test.cc",
-    "resolver/is_storeable_test.cc",
-    "resolver/materialize_test.cc",
-    "resolver/pipeline_overridable_constant_test.cc",
-    "resolver/ptr_ref_test.cc",
-    "resolver/ptr_ref_validation_test.cc",
-    "resolver/resolver_behavior_test.cc",
-    "resolver/resolver_constants_test.cc",
-    "resolver/resolver_test.cc",
-    "resolver/resolver_test_helper.cc",
-    "resolver/resolver_test_helper.h",
-    "resolver/side_effects_test.cc",
-    "resolver/source_variable_test.cc",
-    "resolver/storage_class_layout_validation_test.cc",
-    "resolver/storage_class_validation_test.cc",
-    "resolver/struct_layout_test.cc",
-    "resolver/struct_pipeline_stage_use_test.cc",
-    "resolver/struct_storage_class_use_test.cc",
-    "resolver/type_constructor_validation_test.cc",
-    "resolver/type_validation_test.cc",
-    "resolver/uniformity_test.cc",
-    "resolver/validation_test.cc",
-    "resolver/validator_is_storeable_test.cc",
-    "resolver/var_let_test.cc",
-    "resolver/var_let_validation_test.cc",
-  ]
-  deps = [ ":tint_unittests_ast_src" ]
-}
-
-tint_unittests_source_set("tint_unittests_sem_src") {
-  sources = [
-    "sem/atomic_test.cc",
-    "sem/bool_test.cc",
-    "sem/builtin_test.cc",
-    "sem/constant_test.cc",
-    "sem/depth_multisampled_texture_test.cc",
-    "sem/depth_texture_test.cc",
-    "sem/expression_test.cc",
-    "sem/external_texture_test.cc",
-    "sem/f16_test.cc",
-    "sem/f32_test.cc",
-    "sem/i32_test.cc",
-    "sem/matrix_test.cc",
-    "sem/multisampled_texture_test.cc",
-    "sem/pointer_test.cc",
-    "sem/reference_test.cc",
-    "sem/sampled_texture_test.cc",
-    "sem/sampler_test.cc",
-    "sem/sem_array_test.cc",
-    "sem/sem_struct_test.cc",
-    "sem/storage_texture_test.cc",
-    "sem/texture_test.cc",
-    "sem/type_manager_test.cc",
-    "sem/type_test.cc",
-    "sem/u32_test.cc",
-    "sem/vector_test.cc",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_text_src") {
-  sources = [ "text/unicode_test.cc" ]
-}
-
-tint_unittests_source_set("tint_unittests_transform_src") {
-  sources = [
-    "transform/add_empty_entry_point_test.cc",
-    "transform/add_spirv_block_attribute_test.cc",
-    "transform/array_length_from_uniform_test.cc",
-    "transform/binding_remapper_test.cc",
-    "transform/builtin_polyfill_test.cc",
-    "transform/calculate_array_length_test.cc",
-    "transform/canonicalize_entry_point_io_test.cc",
-    "transform/combine_samplers_test.cc",
-    "transform/decompose_memory_access_test.cc",
-    "transform/decompose_strided_array_test.cc",
-    "transform/decompose_strided_matrix_test.cc",
-    "transform/disable_uniformity_analysis_test.cc",
-    "transform/expand_compound_assignment_test.cc",
-    "transform/first_index_offset_test.cc",
-    "transform/fold_trivial_single_use_lets_test.cc",
-    "transform/for_loop_to_loop_test.cc",
-    "transform/localize_struct_array_assignment_test.cc",
-    "transform/loop_to_for_loop_test.cc",
-    "transform/module_scope_var_to_entry_point_param_test.cc",
-    "transform/multiplanar_external_texture_test.cc",
-    "transform/num_workgroups_from_uniform_test.cc",
-    "transform/promote_initializers_to_const_var_test.cc",
-    "transform/promote_side_effects_to_decl_test.cc",
-    "transform/remove_continue_in_switch_test.cc",
-    "transform/remove_phonies_test.cc",
-    "transform/remove_unreachable_statements_test.cc",
-    "transform/renamer_test.cc",
-    "transform/robustness_test.cc",
-    "transform/simplify_pointers_test.cc",
-    "transform/single_entry_point_test.cc",
-    "transform/test_helper.h",
-    "transform/transform_test.cc",
-    "transform/unshadow_test.cc",
-    "transform/unwind_discard_functions_test.cc",
-    "transform/utils/get_insertion_point_test.cc",
-    "transform/utils/hoist_to_decl_before_test.cc",
-    "transform/var_for_dynamic_index_test.cc",
-    "transform/vectorize_scalar_matrix_constructors_test.cc",
-    "transform/vertex_pulling_test.cc",
-    "transform/while_to_loop_test.cc",
-    "transform/wrap_arrays_in_structs_test.cc",
-    "transform/zero_init_workgroup_memory_test.cc",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_utils_src") {
-  sources = [
-    "utils/bitcast_test.cc",
-    "utils/crc32_test.cc",
-    "utils/defer_test.cc",
-    "utils/enum_set_test.cc",
-    "utils/hash_test.cc",
-    "utils/io/command_test.cc",
-    "utils/io/tmpfile_test.cc",
-    "utils/map_test.cc",
-    "utils/math_test.cc",
-    "utils/result_test.cc",
-    "utils/reverse_test.cc",
-    "utils/scoped_assignment_test.cc",
-    "utils/string_test.cc",
-    "utils/transform_test.cc",
-    "utils/unique_allocator_test.cc",
-    "utils/unique_vector_test.cc",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_writer_src") {
-  sources = [
-    "writer/append_vector_test.cc",
-    "writer/flatten_bindings_test.cc",
-    "writer/float_to_string_test.cc",
-    "writer/generate_external_texture_bindings_test.cc",
-    "writer/text_generator_test.cc",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_spv_reader_src") {
-  sources = [
-    "reader/spirv/enum_converter_test.cc",
-    "reader/spirv/fail_stream_test.cc",
-    "reader/spirv/function_arithmetic_test.cc",
-    "reader/spirv/function_bit_test.cc",
-    "reader/spirv/function_call_test.cc",
-    "reader/spirv/function_cfg_test.cc",
-    "reader/spirv/function_composite_test.cc",
-    "reader/spirv/function_conversion_test.cc",
-    "reader/spirv/function_decl_test.cc",
-    "reader/spirv/function_glsl_std_450_test.cc",
-    "reader/spirv/function_logical_test.cc",
-    "reader/spirv/function_memory_test.cc",
-    "reader/spirv/function_misc_test.cc",
-    "reader/spirv/function_var_test.cc",
-    "reader/spirv/namer_test.cc",
-    "reader/spirv/parser_impl_barrier_test.cc",
-    "reader/spirv/parser_impl_convert_member_decoration_test.cc",
-    "reader/spirv/parser_impl_convert_type_test.cc",
-    "reader/spirv/parser_impl_function_decl_test.cc",
-    "reader/spirv/parser_impl_get_decorations_test.cc",
-    "reader/spirv/parser_impl_handle_test.cc",
-    "reader/spirv/parser_impl_import_test.cc",
-    "reader/spirv/parser_impl_module_var_test.cc",
-    "reader/spirv/parser_impl_named_types_test.cc",
-    "reader/spirv/parser_impl_test.cc",
-    "reader/spirv/parser_impl_test_helper.cc",
-    "reader/spirv/parser_impl_test_helper.h",
-    "reader/spirv/parser_impl_user_name_test.cc",
-    "reader/spirv/parser_test.cc",
-    "reader/spirv/parser_type_test.cc",
-    "reader/spirv/spirv_tools_helpers_test.cc",
-    "reader/spirv/spirv_tools_helpers_test.h",
-    "reader/spirv/usage_test.cc",
-  ]
-
-  deps = [ ":libtint_spv_reader_src" ]
-}
-
-tint_unittests_source_set("tint_unittests_spv_writer_src") {
-  sources = [
-    "writer/spirv/binary_writer_test.cc",
-    "writer/spirv/builder_accessor_expression_test.cc",
-    "writer/spirv/builder_assign_test.cc",
-    "writer/spirv/builder_binary_expression_test.cc",
-    "writer/spirv/builder_bitcast_expression_test.cc",
-    "writer/spirv/builder_block_test.cc",
-    "writer/spirv/builder_builtin_test.cc",
-    "writer/spirv/builder_builtin_texture_test.cc",
-    "writer/spirv/builder_call_test.cc",
-    "writer/spirv/builder_constructor_expression_test.cc",
-    "writer/spirv/builder_discard_test.cc",
-    "writer/spirv/builder_entry_point_test.cc",
-    "writer/spirv/builder_format_conversion_test.cc",
-    "writer/spirv/builder_function_attribute_test.cc",
-    "writer/spirv/builder_function_test.cc",
-    "writer/spirv/builder_function_variable_test.cc",
-    "writer/spirv/builder_global_variable_test.cc",
-    "writer/spirv/builder_ident_expression_test.cc",
-    "writer/spirv/builder_if_test.cc",
-    "writer/spirv/builder_literal_test.cc",
-    "writer/spirv/builder_loop_test.cc",
-    "writer/spirv/builder_return_test.cc",
-    "writer/spirv/builder_switch_test.cc",
-    "writer/spirv/builder_test.cc",
-    "writer/spirv/builder_type_test.cc",
-    "writer/spirv/builder_unary_op_expression_test.cc",
-    "writer/spirv/instruction_test.cc",
-    "writer/spirv/operand_test.cc",
-    "writer/spirv/scalar_constant_test.cc",
-    "writer/spirv/spv_dump.cc",
-    "writer/spirv/spv_dump.h",
-    "writer/spirv/test_helper.h",
-  ]
-
-  deps = [
-    ":libtint_spv_writer_src",
-    ":tint_unittests_ast_src",
-    "${tint_spirv_tools_dir}/:spvtools",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_wgsl_reader_src") {
-  sources = [
-    "reader/wgsl/lexer_test.cc",
-    "reader/wgsl/parser_impl_additive_expression_test.cc",
-    "reader/wgsl/parser_impl_and_expression_test.cc",
-    "reader/wgsl/parser_impl_argument_expression_list_test.cc",
-    "reader/wgsl/parser_impl_assignment_stmt_test.cc",
-    "reader/wgsl/parser_impl_body_stmt_test.cc",
-    "reader/wgsl/parser_impl_break_stmt_test.cc",
-    "reader/wgsl/parser_impl_bug_cases_test.cc",
-    "reader/wgsl/parser_impl_call_stmt_test.cc",
-    "reader/wgsl/parser_impl_case_body_test.cc",
-    "reader/wgsl/parser_impl_const_expr_test.cc",
-    "reader/wgsl/parser_impl_const_literal_test.cc",
-    "reader/wgsl/parser_impl_continue_stmt_test.cc",
-    "reader/wgsl/parser_impl_continuing_stmt_test.cc",
-    "reader/wgsl/parser_impl_depth_texture_test.cc",
-    "reader/wgsl/parser_impl_enable_directive_test.cc",
-    "reader/wgsl/parser_impl_equality_expression_test.cc",
-    "reader/wgsl/parser_impl_error_msg_test.cc",
-    "reader/wgsl/parser_impl_error_resync_test.cc",
-    "reader/wgsl/parser_impl_exclusive_or_expression_test.cc",
-    "reader/wgsl/parser_impl_external_texture_test.cc",
-    "reader/wgsl/parser_impl_for_stmt_test.cc",
-    "reader/wgsl/parser_impl_function_attribute_list_test.cc",
-    "reader/wgsl/parser_impl_function_attribute_test.cc",
-    "reader/wgsl/parser_impl_function_decl_test.cc",
-    "reader/wgsl/parser_impl_function_header_test.cc",
-    "reader/wgsl/parser_impl_global_constant_decl_test.cc",
-    "reader/wgsl/parser_impl_global_decl_test.cc",
-    "reader/wgsl/parser_impl_global_variable_decl_test.cc",
-    "reader/wgsl/parser_impl_if_stmt_test.cc",
-    "reader/wgsl/parser_impl_inclusive_or_expression_test.cc",
-    "reader/wgsl/parser_impl_increment_decrement_stmt_test.cc",
-    "reader/wgsl/parser_impl_logical_and_expression_test.cc",
-    "reader/wgsl/parser_impl_logical_or_expression_test.cc",
-    "reader/wgsl/parser_impl_loop_stmt_test.cc",
-    "reader/wgsl/parser_impl_multiplicative_expression_test.cc",
-    "reader/wgsl/parser_impl_param_list_test.cc",
-    "reader/wgsl/parser_impl_paren_rhs_stmt_test.cc",
-    "reader/wgsl/parser_impl_pipeline_stage_test.cc",
-    "reader/wgsl/parser_impl_primary_expression_test.cc",
-    "reader/wgsl/parser_impl_relational_expression_test.cc",
-    "reader/wgsl/parser_impl_reserved_keyword_test.cc",
-    "reader/wgsl/parser_impl_sampled_texture_test.cc",
-    "reader/wgsl/parser_impl_sampler_test.cc",
-    "reader/wgsl/parser_impl_shift_expression_test.cc",
-    "reader/wgsl/parser_impl_singular_expression_test.cc",
-    "reader/wgsl/parser_impl_statement_test.cc",
-    "reader/wgsl/parser_impl_statements_test.cc",
-    "reader/wgsl/parser_impl_storage_class_test.cc",
-    "reader/wgsl/parser_impl_storage_texture_test.cc",
-    "reader/wgsl/parser_impl_struct_attribute_decl_test.cc",
-    "reader/wgsl/parser_impl_struct_body_decl_test.cc",
-    "reader/wgsl/parser_impl_struct_decl_test.cc",
-    "reader/wgsl/parser_impl_struct_member_attribute_decl_test.cc",
-    "reader/wgsl/parser_impl_struct_member_attribute_test.cc",
-    "reader/wgsl/parser_impl_struct_member_test.cc",
-    "reader/wgsl/parser_impl_switch_body_test.cc",
-    "reader/wgsl/parser_impl_switch_stmt_test.cc",
-    "reader/wgsl/parser_impl_test.cc",
-    "reader/wgsl/parser_impl_test_helper.cc",
-    "reader/wgsl/parser_impl_test_helper.h",
-    "reader/wgsl/parser_impl_texel_format_test.cc",
-    "reader/wgsl/parser_impl_texture_sampler_test.cc",
-    "reader/wgsl/parser_impl_type_alias_test.cc",
-    "reader/wgsl/parser_impl_type_decl_test.cc",
-    "reader/wgsl/parser_impl_unary_expression_test.cc",
-    "reader/wgsl/parser_impl_variable_attribute_list_test.cc",
-    "reader/wgsl/parser_impl_variable_attribute_test.cc",
-    "reader/wgsl/parser_impl_variable_decl_test.cc",
-    "reader/wgsl/parser_impl_variable_ident_decl_test.cc",
-    "reader/wgsl/parser_impl_variable_qualifier_test.cc",
-    "reader/wgsl/parser_impl_variable_stmt_test.cc",
-    "reader/wgsl/parser_impl_while_stmt_test.cc",
-    "reader/wgsl/parser_test.cc",
-    "reader/wgsl/token_test.cc",
-  ]
-
-  deps = [ ":libtint_wgsl_reader_src" ]
-}
-
-tint_unittests_source_set("tint_unittests_wgsl_writer_src") {
-  sources = [
-    "writer/wgsl/generator_impl_alias_type_test.cc",
-    "writer/wgsl/generator_impl_array_accessor_test.cc",
-    "writer/wgsl/generator_impl_assign_test.cc",
-    "writer/wgsl/generator_impl_binary_test.cc",
-    "writer/wgsl/generator_impl_bitcast_test.cc",
-    "writer/wgsl/generator_impl_block_test.cc",
-    "writer/wgsl/generator_impl_break_test.cc",
-    "writer/wgsl/generator_impl_call_test.cc",
-    "writer/wgsl/generator_impl_case_test.cc",
-    "writer/wgsl/generator_impl_cast_test.cc",
-    "writer/wgsl/generator_impl_constructor_test.cc",
-    "writer/wgsl/generator_impl_continue_test.cc",
-    "writer/wgsl/generator_impl_discard_test.cc",
-    "writer/wgsl/generator_impl_enable_test.cc",
-    "writer/wgsl/generator_impl_fallthrough_test.cc",
-    "writer/wgsl/generator_impl_function_test.cc",
-    "writer/wgsl/generator_impl_global_decl_test.cc",
-    "writer/wgsl/generator_impl_identifier_test.cc",
-    "writer/wgsl/generator_impl_if_test.cc",
-    "writer/wgsl/generator_impl_literal_test.cc",
-    "writer/wgsl/generator_impl_loop_test.cc",
-    "writer/wgsl/generator_impl_member_accessor_test.cc",
-    "writer/wgsl/generator_impl_return_test.cc",
-    "writer/wgsl/generator_impl_switch_test.cc",
-    "writer/wgsl/generator_impl_test.cc",
-    "writer/wgsl/generator_impl_type_test.cc",
-    "writer/wgsl/generator_impl_unary_op_test.cc",
-    "writer/wgsl/generator_impl_variable_decl_statement_test.cc",
-    "writer/wgsl/generator_impl_variable_test.cc",
-    "writer/wgsl/test_helper.h",
-  ]
-
-  deps = [
-    ":libtint_wgsl_writer_src",
-    ":tint_unittests_ast_src",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_msl_writer_src") {
-  sources = [
-    "writer/msl/generator_impl_array_accessor_test.cc",
-    "writer/msl/generator_impl_assign_test.cc",
-    "writer/msl/generator_impl_binary_test.cc",
-    "writer/msl/generator_impl_bitcast_test.cc",
-    "writer/msl/generator_impl_block_test.cc",
-    "writer/msl/generator_impl_break_test.cc",
-    "writer/msl/generator_impl_builtin_test.cc",
-    "writer/msl/generator_impl_builtin_texture_test.cc",
-    "writer/msl/generator_impl_call_test.cc",
-    "writer/msl/generator_impl_case_test.cc",
-    "writer/msl/generator_impl_cast_test.cc",
-    "writer/msl/generator_impl_constructor_test.cc",
-    "writer/msl/generator_impl_continue_test.cc",
-    "writer/msl/generator_impl_discard_test.cc",
-    "writer/msl/generator_impl_function_test.cc",
-    "writer/msl/generator_impl_identifier_test.cc",
-    "writer/msl/generator_impl_if_test.cc",
-    "writer/msl/generator_impl_import_test.cc",
-    "writer/msl/generator_impl_loop_test.cc",
-    "writer/msl/generator_impl_member_accessor_test.cc",
-    "writer/msl/generator_impl_module_constant_test.cc",
-    "writer/msl/generator_impl_return_test.cc",
-    "writer/msl/generator_impl_sanitizer_test.cc",
-    "writer/msl/generator_impl_switch_test.cc",
-    "writer/msl/generator_impl_test.cc",
-    "writer/msl/generator_impl_type_test.cc",
-    "writer/msl/generator_impl_unary_op_test.cc",
-    "writer/msl/generator_impl_variable_decl_statement_test.cc",
-    "writer/msl/test_helper.h",
-  ]
-
-  deps = [
-    ":libtint_msl_writer_src",
-    ":tint_unittests_ast_src",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_hlsl_writer_src") {
-  sources = [
-    "writer/hlsl/generator_impl_array_accessor_test.cc",
-    "writer/hlsl/generator_impl_assign_test.cc",
-    "writer/hlsl/generator_impl_binary_test.cc",
-    "writer/hlsl/generator_impl_bitcast_test.cc",
-    "writer/hlsl/generator_impl_block_test.cc",
-    "writer/hlsl/generator_impl_break_test.cc",
-    "writer/hlsl/generator_impl_builtin_test.cc",
-    "writer/hlsl/generator_impl_builtin_texture_test.cc",
-    "writer/hlsl/generator_impl_call_test.cc",
-    "writer/hlsl/generator_impl_case_test.cc",
-    "writer/hlsl/generator_impl_cast_test.cc",
-    "writer/hlsl/generator_impl_constructor_test.cc",
-    "writer/hlsl/generator_impl_continue_test.cc",
-    "writer/hlsl/generator_impl_discard_test.cc",
-    "writer/hlsl/generator_impl_function_test.cc",
-    "writer/hlsl/generator_impl_identifier_test.cc",
-    "writer/hlsl/generator_impl_if_test.cc",
-    "writer/hlsl/generator_impl_import_test.cc",
-    "writer/hlsl/generator_impl_loop_test.cc",
-    "writer/hlsl/generator_impl_member_accessor_test.cc",
-    "writer/hlsl/generator_impl_module_constant_test.cc",
-    "writer/hlsl/generator_impl_return_test.cc",
-    "writer/hlsl/generator_impl_sanitizer_test.cc",
-    "writer/hlsl/generator_impl_switch_test.cc",
-    "writer/hlsl/generator_impl_test.cc",
-    "writer/hlsl/generator_impl_type_test.cc",
-    "writer/hlsl/generator_impl_unary_op_test.cc",
-    "writer/hlsl/generator_impl_variable_decl_statement_test.cc",
-    "writer/hlsl/generator_impl_workgroup_var_test.cc",
-    "writer/hlsl/test_helper.h",
-  ]
-
-  deps = [
-    ":libtint_hlsl_writer_src",
-    ":tint_unittests_ast_src",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_glsl_writer_src") {
-  sources = [
-    "writer/glsl/generator_impl_array_accessor_test.cc",
-    "writer/glsl/generator_impl_assign_test.cc",
-    "writer/glsl/generator_impl_binary_test.cc",
-    "writer/glsl/generator_impl_bitcast_test.cc",
-    "writer/glsl/generator_impl_block_test.cc",
-    "writer/glsl/generator_impl_break_test.cc",
-    "writer/glsl/generator_impl_builtin_test.cc",
-    "writer/glsl/generator_impl_builtin_texture_test.cc",
-    "writer/glsl/generator_impl_call_test.cc",
-    "writer/glsl/generator_impl_case_test.cc",
-    "writer/glsl/generator_impl_cast_test.cc",
-    "writer/glsl/generator_impl_constructor_test.cc",
-    "writer/glsl/generator_impl_continue_test.cc",
-    "writer/glsl/generator_impl_discard_test.cc",
-    "writer/glsl/generator_impl_function_test.cc",
-    "writer/glsl/generator_impl_identifier_test.cc",
-    "writer/glsl/generator_impl_if_test.cc",
-    "writer/glsl/generator_impl_import_test.cc",
-    "writer/glsl/generator_impl_loop_test.cc",
-    "writer/glsl/generator_impl_member_accessor_test.cc",
-    "writer/glsl/generator_impl_module_constant_test.cc",
-    "writer/glsl/generator_impl_return_test.cc",
-    "writer/glsl/generator_impl_sanitizer_test.cc",
-    "writer/glsl/generator_impl_storage_buffer_test.cc",
-    "writer/glsl/generator_impl_switch_test.cc",
-    "writer/glsl/generator_impl_test.cc",
-    "writer/glsl/generator_impl_type_test.cc",
-    "writer/glsl/generator_impl_unary_op_test.cc",
-    "writer/glsl/generator_impl_uniform_buffer_test.cc",
-    "writer/glsl/generator_impl_variable_decl_statement_test.cc",
-    "writer/glsl/generator_impl_workgroup_var_test.cc",
-    "writer/glsl/test_helper.h",
-  ]
-
-  deps = [
-    ":libtint_glsl_writer_src",
-    ":tint_unittests_ast_src",
-    ":tint_unittests_transform_src",
-  ]
-}
-
-tint_unittests_source_set("tint_unittests_core_src") {
-  sources = [
-    "castable_test.cc",
-    "clone_context_test.cc",
-    "debug_test.cc",
-    "demangler_test.cc",
-    "number_test.cc",
-    "program_builder_test.cc",
-    "program_test.cc",
-    "scope_stack_test.cc",
-    "source_test.cc",
-    "symbol_table_test.cc",
-    "symbol_test.cc",
-    "traits_test.cc",
-    "utils/block_allocator_test.cc",
-  ]
-
-  deps = [ ":tint_unittests_ast_src" ]
-}
-
-if (build_with_chromium) {
-  tint_unittests_source_set("tint_unittests_fuzzer_src") {
-    sources = [ "fuzzers/random_generator_test.cc" ]
-
-    deps = [
-      ":tint_unittests_core_src",
-      "fuzzers:tint_fuzzer_common_src",
-    ]
-  }
-}
-
-source_set("tint_unittests_src") {
-  testonly = true
-
-  deps = [
-    ":libtint_wgsl_reader_src",
-    ":libtint_wgsl_writer_src",
-    ":tint_unittests_ast_src",
-    ":tint_unittests_core_src",
-    ":tint_unittests_diagnostic_src",
-    ":tint_unittests_inspector_src",
-    ":tint_unittests_resolver_src",
-    ":tint_unittests_sem_src",
-    ":tint_unittests_text_src",
-    ":tint_unittests_transform_src",
-    ":tint_unittests_utils_src",
-    ":tint_unittests_writer_src",
-  ]
-
-  if (tint_build_spv_reader) {
-    deps += [ ":tint_unittests_spv_reader_src" ]
-  }
-
-  if (tint_build_spv_writer) {
-    deps += [ ":tint_unittests_spv_writer_src" ]
-  }
-
-  if (tint_build_wgsl_reader) {
-    deps += [ ":tint_unittests_wgsl_reader_src" ]
-  }
-
-  if (tint_build_wgsl_writer) {
-    deps += [ ":tint_unittests_wgsl_writer_src" ]
-  }
-
-  if (tint_build_msl_writer) {
-    deps += [ ":tint_unittests_msl_writer_src" ]
-  }
-
-  if (tint_build_hlsl_writer) {
-    deps += [ ":tint_unittests_hlsl_writer_src" ]
-  }
-
-  if (tint_build_glsl_writer) {
-    deps += [ ":tint_unittests_glsl_writer_src" ]
-  }
-
-  if (build_with_chromium) {
-    deps += [ ":tint_unittests_fuzzer_src" ]
-  }
-
-  configs += [ ":tint_unittests_config" ]
-
-  if (build_with_chromium) {
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-  }
-}
-
-test("tint_unittests") {
-  deps = [
-    ":gmock_and_gtest",
-    ":tint_unittests_src",
-    "${tint_spirv_tools_dir}/:spvtools",
-    "${tint_spirv_tools_dir}/:spvtools_opt",
-    "${tint_spirv_tools_dir}/:spvtools_val",
-  ]
-
-  deps += [ ":tint_unittests_main" ]
-
-  configs += [ ":tint_unittests_config" ]
-
-  if (build_with_chromium) {
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-  }
-
-  testonly = true
-}
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index f7fb14a..3e7154c 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -22,18 +22,6 @@
     ${spirv-tools_SOURCE_DIR}
     ${spirv-tools_BINARY_DIR}
   )
-
-  if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
-    # The SPIRV-Tools code is conditioned against C++ and an older version of Clang.
-    # Suppress warnings triggered in our current compilation environment.
-    # TODO(dneto): Fix the issues upstream.
-    target_compile_options(${TARGET} PRIVATE
-      -Wno-newline-eof
-      -Wno-sign-conversion
-      -Wno-old-style-cast
-      -Wno-weak-vtables
-    )
-  endif()
 endfunction()
 
 ## Tint diagnostic utilities. Used by libtint and tint_utils_io.
@@ -99,6 +87,8 @@
   ast/case_statement.h
   ast/compound_assignment_statement.cc
   ast/compound_assignment_statement.h
+  ast/const.cc
+  ast/const.h
   ast/continue_statement.cc
   ast/continue_statement.h
   ast/depth_multisampled_texture.cc
@@ -324,6 +314,8 @@
   sem/i32.h
   sem/if_statement.cc
   sem/if_statement.h
+  sem/index_accessor_expression.cc
+  sem/index_accessor_expression.h
   sem/info.cc
   sem/info.h
   sem/loop_statement.cc
@@ -428,8 +420,8 @@
   transform/multiplanar_external_texture.h
   transform/num_workgroups_from_uniform.cc
   transform/num_workgroups_from_uniform.h
-  transform/promote_initializers_to_const_var.cc
-  transform/promote_initializers_to_const_var.h
+  transform/promote_initializers_to_let.cc
+  transform/promote_initializers_to_let.h
   transform/promote_side_effects_to_decl.cc
   transform/promote_side_effects_to_decl.h
   transform/remove_continue_in_switch.cc
@@ -446,6 +438,8 @@
   transform/simplify_pointers.h
   transform/single_entry_point.cc
   transform/single_entry_point.h
+  transform/spirv_atomic.cc
+  transform/spirv_atomic.h
   transform/transform.cc
   transform/transform.h
   transform/unshadow.cc
@@ -464,8 +458,6 @@
   transform/vertex_pulling.h
   transform/while_to_loop.cc
   transform/while_to_loop.h
-  transform/wrap_arrays_in_structs.cc
-  transform/wrap_arrays_in_structs.h
   transform/zero_init_workgroup_memory.cc
   transform/zero_init_workgroup_memory.h
   utils/bitcast.h
@@ -812,13 +804,12 @@
     resolver/type_validation_test.cc
     resolver/validation_test.cc
     resolver/validator_is_storeable_test.cc
-    resolver/var_let_test.cc
-    resolver/var_let_validation_test.cc
+    resolver/variable_test.cc
+    resolver/variable_validation_test.cc
     scope_stack_test.cc
     sem/atomic.cc
     sem/bool_test.cc
     sem/builtin_test.cc
-    sem/constant_test.cc
     sem/depth_multisampled_texture_test.cc
     sem/depth_texture_test.cc
     sem/expression_test.cc
@@ -940,7 +931,6 @@
       reader/wgsl/parser_impl_bug_cases_test.cc
       reader/wgsl/parser_impl_call_stmt_test.cc
       reader/wgsl/parser_impl_case_body_test.cc
-      reader/wgsl/parser_impl_const_expr_test.cc
       reader/wgsl/parser_impl_const_literal_test.cc
       reader/wgsl/parser_impl_continue_stmt_test.cc
       reader/wgsl/parser_impl_continuing_stmt_test.cc
@@ -1103,7 +1093,7 @@
       transform/module_scope_var_to_entry_point_param_test.cc
       transform/multiplanar_external_texture_test.cc
       transform/num_workgroups_from_uniform_test.cc
-      transform/promote_initializers_to_const_var_test.cc
+      transform/promote_initializers_to_let_test.cc
       transform/promote_side_effects_to_decl_test.cc
       transform/remove_continue_in_switch_test.cc
       transform/remove_phonies_test.cc
@@ -1112,6 +1102,7 @@
       transform/robustness_test.cc
       transform/simplify_pointers_test.cc
       transform/single_entry_point_test.cc
+      transform/spirv_atomic_test.cc
       transform/test_helper.h
       transform/unshadow_test.cc
       transform/unwind_discard_functions_test.cc
@@ -1119,7 +1110,6 @@
       transform/vectorize_scalar_matrix_constructors_test.cc
       transform/vertex_pulling_test.cc
       transform/while_to_loop_test.cc
-      transform/wrap_arrays_in_structs_test.cc
       transform/zero_init_workgroup_memory_test.cc
       transform/utils/get_insertion_point_test.cc
       transform/utils/hoist_to_decl_before_test.cc
diff --git a/src/tint/ast/array_test.cc b/src/tint/ast/array_test.cc
index baed079..f396e0c 100644
--- a/src/tint/ast/array_test.cc
+++ b/src/tint/ast/array_test.cc
@@ -62,7 +62,7 @@
 
 TEST_F(AstArrayTest, FriendlyName_WithStride) {
     auto* i32 = create<I32>();
-    auto* arr = create<Array>(i32, Expr(5_u), AttributeList{create<StrideAttribute>(32)});
+    auto* arr = create<Array>(i32, Expr(5_u), AttributeList{create<StrideAttribute>(32u)});
     EXPECT_EQ(arr->FriendlyName(Symbols()), "@stride(32) array<i32, 5>");
 }
 
diff --git a/src/tint/ast/binding_attribute_test.cc b/src/tint/ast/binding_attribute_test.cc
index f51fc25..8000933 100644
--- a/src/tint/ast/binding_attribute_test.cc
+++ b/src/tint/ast/binding_attribute_test.cc
@@ -20,7 +20,7 @@
 using BindingAttributeTest = TestHelper;
 
 TEST_F(BindingAttributeTest, Creation) {
-    auto* d = create<BindingAttribute>(2);
+    auto* d = create<BindingAttribute>(2u);
     EXPECT_EQ(2u, d->value);
 }
 
diff --git a/src/tint/ast/builtin_texture_helper_test.cc b/src/tint/ast/builtin_texture_helper_test.cc
index 21cbd93..ad42267 100644
--- a/src/tint/ast/builtin_texture_helper_test.cc
+++ b/src/tint/ast/builtin_texture_helper_test.cc
@@ -142,30 +142,31 @@
 
 const ast::Variable* TextureOverloadCase::BuildTextureVariable(ProgramBuilder* b) const {
     AttributeList attrs = {
-        b->create<ast::GroupAttribute>(0),
-        b->create<ast::BindingAttribute>(0),
+        b->create<ast::GroupAttribute>(0u),
+        b->create<ast::BindingAttribute>(0u),
     };
     switch (texture_kind) {
         case ast::builtin::test::TextureKind::kRegular:
-            return b->Global(
+            return b->GlobalVar(
                 "texture",
                 b->ty.sampled_texture(texture_dimension, BuildResultVectorComponentType(b)), attrs);
 
         case ast::builtin::test::TextureKind::kDepth:
-            return b->Global("texture", b->ty.depth_texture(texture_dimension), attrs);
+            return b->GlobalVar("texture", b->ty.depth_texture(texture_dimension), attrs);
 
         case ast::builtin::test::TextureKind::kDepthMultisampled:
-            return b->Global("texture", b->ty.depth_multisampled_texture(texture_dimension), attrs);
+            return b->GlobalVar("texture", b->ty.depth_multisampled_texture(texture_dimension),
+                                attrs);
 
         case ast::builtin::test::TextureKind::kMultisampled:
-            return b->Global(
+            return b->GlobalVar(
                 "texture",
                 b->ty.multisampled_texture(texture_dimension, BuildResultVectorComponentType(b)),
                 attrs);
 
         case ast::builtin::test::TextureKind::kStorage: {
             auto* st = b->ty.storage_texture(texture_dimension, texel_format, access);
-            return b->Global("texture", st, attrs);
+            return b->GlobalVar("texture", st, attrs);
         }
     }
 
@@ -175,10 +176,10 @@
 
 const ast::Variable* TextureOverloadCase::BuildSamplerVariable(ProgramBuilder* b) const {
     AttributeList attrs = {
-        b->create<ast::GroupAttribute>(0),
-        b->create<ast::BindingAttribute>(1),
+        b->create<ast::GroupAttribute>(0u),
+        b->create<ast::BindingAttribute>(1u),
     };
-    return b->Global("sampler", b->ty.sampler(sampler_kind), attrs);
+    return b->GlobalVar("sampler", b->ty.sampler(sampler_kind), attrs);
 }
 
 std::vector<TextureOverloadCase> TextureOverloadCase::ValidCases() {
diff --git a/src/tint/ast/const.cc b/src/tint/ast/const.cc
new file mode 100644
index 0000000..e13cc25
--- /dev/null
+++ b/src/tint/ast/const.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 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/ast/const.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Const);
+
+namespace tint::ast {
+
+Const::Const(ProgramID pid,
+             const Source& src,
+             const Symbol& sym,
+             const ast::Type* ty,
+             const Expression* ctor,
+             AttributeList attrs)
+    : Base(pid, src, sym, ty, ctor, attrs) {
+    TINT_ASSERT(AST, ctor != nullptr);
+}
+
+Const::Const(Const&&) = default;
+
+Const::~Const() = default;
+
+const char* Const::Kind() const {
+    return "const";
+}
+
+const Const* Const::Clone(CloneContext* ctx) const {
+    auto src = ctx->Clone(source);
+    auto sym = ctx->Clone(symbol);
+    auto* ty = ctx->Clone(type);
+    auto* ctor = ctx->Clone(constructor);
+    auto attrs = ctx->Clone(attributes);
+    return ctx->dst->create<Const>(src, sym, ty, ctor, attrs);
+}
+
+}  // namespace tint::ast
diff --git a/src/tint/ast/const.h b/src/tint/ast/const.h
new file mode 100644
index 0000000..32e3ddd
--- /dev/null
+++ b/src/tint/ast/const.h
@@ -0,0 +1,67 @@
+// Copyright 2022 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_AST_CONST_H_
+#define SRC_TINT_AST_CONST_H_
+
+#include "src/tint/ast/variable.h"
+
+namespace tint::ast {
+
+/// A "const" declaration is a name for a module-scoped or function-scoped creation-time value.
+/// const must have a constructor expression.
+///
+/// Examples:
+///
+/// ```
+///   const n  = 123;                           // Abstract-integer typed constant
+///   const pi = 3.14159265359;                 // Abstract-float typed constant
+///   const max_f32 : f32 = 0x1.fffffep+127;    // f32 typed constant
+/// ```
+/// @see https://www.w3.org/TR/WGSL/#creation-time-consts
+class Const final : public Castable<Const, Variable> {
+  public:
+    /// Create a 'const' creation-time value variable.
+    /// @param program_id the identifier of the program that owns this node
+    /// @param source the variable source
+    /// @param sym the variable symbol
+    /// @param type the declared variable type
+    /// @param constructor the constructor expression. Must not be nullptr.
+    /// @param attributes the variable attributes
+    Const(ProgramID program_id,
+          const Source& source,
+          const Symbol& sym,
+          const ast::Type* type,
+          const Expression* constructor,
+          AttributeList attributes);
+
+    /// Move constructor
+    Const(Const&&);
+
+    /// Destructor
+    ~Const() override;
+
+    /// @returns "const"
+    const char* Kind() const override;
+
+    /// Clones this node and all transitive child nodes using the `CloneContext`
+    /// `ctx`.
+    /// @param ctx the clone context
+    /// @return the newly cloned node
+    const Const* Clone(CloneContext* ctx) const override;
+};
+
+}  // namespace tint::ast
+
+#endif  // SRC_TINT_AST_CONST_H_
diff --git a/src/tint/ast/group_attribute_test.cc b/src/tint/ast/group_attribute_test.cc
index 53167bb..4a6dd1d 100644
--- a/src/tint/ast/group_attribute_test.cc
+++ b/src/tint/ast/group_attribute_test.cc
@@ -20,7 +20,7 @@
 using GroupAttributeTest = TestHelper;
 
 TEST_F(GroupAttributeTest, Creation) {
-    auto* d = create<GroupAttribute>(2);
+    auto* d = create<GroupAttribute>(2u);
     EXPECT_EQ(2u, d->value);
 }
 
diff --git a/src/tint/ast/id_attribute_test.cc b/src/tint/ast/id_attribute_test.cc
index 6957d66..ad05c58 100644
--- a/src/tint/ast/id_attribute_test.cc
+++ b/src/tint/ast/id_attribute_test.cc
@@ -22,7 +22,7 @@
 using IdAttributeTest = TestHelper;
 
 TEST_F(IdAttributeTest, Creation) {
-    auto* d = create<IdAttribute>(12);
+    auto* d = create<IdAttribute>(12u);
     EXPECT_EQ(12u, d->value);
 }
 
diff --git a/src/tint/ast/let.cc b/src/tint/ast/let.cc
index e771c4d..8db8ec1 100644
--- a/src/tint/ast/let.cc
+++ b/src/tint/ast/let.cc
@@ -34,6 +34,10 @@
 
 Let::~Let() = default;
 
+const char* Let::Kind() const {
+    return "let";
+}
+
 const Let* Let::Clone(CloneContext* ctx) const {
     auto src = ctx->Clone(source);
     auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/let.h b/src/tint/ast/let.h
index 2b0a6aa..2c1ad7c 100644
--- a/src/tint/ast/let.h
+++ b/src/tint/ast/let.h
@@ -49,6 +49,9 @@
     /// Destructor
     ~Let() override;
 
+    /// @returns "let"
+    const char* Kind() const override;
+
     /// Clones this node and all transitive child nodes using the `CloneContext`
     /// `ctx`.
     /// @param ctx the clone context
diff --git a/src/tint/ast/location_attribute_test.cc b/src/tint/ast/location_attribute_test.cc
index a1562d5..e0bcb39 100644
--- a/src/tint/ast/location_attribute_test.cc
+++ b/src/tint/ast/location_attribute_test.cc
@@ -20,7 +20,7 @@
 using LocationAttributeTest = TestHelper;
 
 TEST_F(LocationAttributeTest, Creation) {
-    auto* d = create<LocationAttribute>(2);
+    auto* d = create<LocationAttribute>(2u);
     EXPECT_EQ(2u, d->value);
 }
 
diff --git a/src/tint/ast/matrix_test.cc b/src/tint/ast/matrix_test.cc
index 66ea84d..dbb88e3 100644
--- a/src/tint/ast/matrix_test.cc
+++ b/src/tint/ast/matrix_test.cc
@@ -34,7 +34,7 @@
 
 TEST_F(AstMatrixTest, Creation) {
     auto* i32 = create<I32>();
-    auto* m = create<Matrix>(i32, 2, 4);
+    auto* m = create<Matrix>(i32, 2u, 4u);
     EXPECT_EQ(m->type, i32);
     EXPECT_EQ(m->rows, 2u);
     EXPECT_EQ(m->columns, 4u);
@@ -42,12 +42,12 @@
 
 TEST_F(AstMatrixTest, FriendlyName) {
     auto* i32 = create<I32>();
-    auto* m = create<Matrix>(i32, 3, 2);
+    auto* m = create<Matrix>(i32, 3u, 2u);
     EXPECT_EQ(m->FriendlyName(Symbols()), "mat2x3<i32>");
 }
 
 TEST_F(AstMatrixTest, FriendlyName_WithoutType) {
-    auto* m = create<Matrix>(nullptr, 3, 2);
+    auto* m = create<Matrix>(nullptr, 3u, 2u);
     EXPECT_EQ(m->FriendlyName(Symbols()), "mat2x3");
 }
 
diff --git a/src/tint/ast/module_clone_test.cc b/src/tint/ast/module_clone_test.cc
index bd96b26..544e6bf 100644
--- a/src/tint/ast/module_clone_test.cc
+++ b/src/tint/ast/module_clone_test.cc
@@ -37,8 +37,8 @@
   m1 : array<u32, 6>,
 };
 
-let c0 : i32 = 10;
-let c1 : bool = true;
+const c0 : i32 = 10;
+const c1 : bool = true;
 
 type t0 = array<vec4<f32>>;
 type t1 = array<vec4<f32>>;
@@ -71,6 +71,8 @@
   var l4 : S1;
   var l5 : u32 = l4.m1[5];
   let l6 : ptr<private, u32> = &g0;
+  const l7 = 123;
+  const l8 : i32 = 123;
   loop {
     l0 = (p1 + 2);
     if (((l0 % 4) == 0)) {
@@ -104,7 +106,7 @@
   f1(1.0, 2);
 }
 
-let declaration_order_check_0 : i32 = 1;
+const declaration_order_check_0 : i32 = 1;
 
 type declaration_order_check_1 = f32;
 
@@ -112,7 +114,7 @@
 
 type declaration_order_check_3 = f32;
 
-let declaration_order_check_4 : i32 = 1;
+const declaration_order_check_4 : i32 = 1;
 
 )");
 
diff --git a/src/tint/ast/module_test.cc b/src/tint/ast/module_test.cc
index 4a2884b..7096769 100644
--- a/src/tint/ast/module_test.cc
+++ b/src/tint/ast/module_test.cc
@@ -92,7 +92,7 @@
         ProgramBuilder b;
         b.Func("F", {}, b.ty.void_(), {});
         b.Alias("A", b.ty.u32());
-        b.Global("V", b.ty.i32(), ast::StorageClass::kPrivate);
+        b.GlobalVar("V", b.ty.i32(), ast::StorageClass::kPrivate);
         return Program(std::move(b));
     }();
 
diff --git a/src/tint/ast/override.cc b/src/tint/ast/override.cc
index f494bc8..efd048d 100644
--- a/src/tint/ast/override.cc
+++ b/src/tint/ast/override.cc
@@ -32,6 +32,10 @@
 
 Override::~Override() = default;
 
+const char* Override::Kind() const {
+    return "override";
+}
+
 const Override* Override::Clone(CloneContext* ctx) const {
     auto src = ctx->Clone(source);
     auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/override.h b/src/tint/ast/override.h
index 168b9b2..98319e5 100644
--- a/src/tint/ast/override.h
+++ b/src/tint/ast/override.h
@@ -50,6 +50,9 @@
     /// Destructor
     ~Override() override;
 
+    /// @returns "override"
+    const char* Kind() const override;
+
     /// Clones this node and all transitive child nodes using the `CloneContext`
     /// `ctx`.
     /// @param ctx the clone context
diff --git a/src/tint/ast/parameter.cc b/src/tint/ast/parameter.cc
index b7ea3b1..ea3c51f 100644
--- a/src/tint/ast/parameter.cc
+++ b/src/tint/ast/parameter.cc
@@ -31,6 +31,10 @@
 
 Parameter::~Parameter() = default;
 
+const char* Parameter::Kind() const {
+    return "parameter";
+}
+
 const Parameter* Parameter::Clone(CloneContext* ctx) const {
     auto src = ctx->Clone(source);
     auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/parameter.h b/src/tint/ast/parameter.h
index d3ecf8d..eb4b688 100644
--- a/src/tint/ast/parameter.h
+++ b/src/tint/ast/parameter.h
@@ -51,6 +51,9 @@
     /// Destructor
     ~Parameter() override;
 
+    /// @returns "parameter"
+    const char* Kind() const override;
+
     /// Clones this node and all transitive child nodes using the `CloneContext`
     /// `ctx`.
     /// @param ctx the clone context
diff --git a/src/tint/ast/stride_attribute_test.cc b/src/tint/ast/stride_attribute_test.cc
index 61c4fb5..eb19034 100644
--- a/src/tint/ast/stride_attribute_test.cc
+++ b/src/tint/ast/stride_attribute_test.cc
@@ -20,13 +20,13 @@
 using StrideAttributeTest = TestHelper;
 
 TEST_F(StrideAttributeTest, Creation) {
-    auto* d = create<StrideAttribute>(2);
+    auto* d = create<StrideAttribute>(2u);
     EXPECT_EQ(2u, d->stride);
 }
 
 TEST_F(StrideAttributeTest, Source) {
     auto* d = create<StrideAttribute>(
-        Source{Source::Range{Source::Location{1, 2}, Source::Location{3, 4}}}, 2);
+        Source{Source::Range{Source::Location{1, 2}, Source::Location{3, 4}}}, 2u);
     EXPECT_EQ(d->source.range.begin.line, 1u);
     EXPECT_EQ(d->source.range.begin.column, 2u);
     EXPECT_EQ(d->source.range.end.line, 3u);
diff --git a/src/tint/ast/struct_member_align_attribute_test.cc b/src/tint/ast/struct_member_align_attribute_test.cc
index 5b4ff48..ba4d1bb 100644
--- a/src/tint/ast/struct_member_align_attribute_test.cc
+++ b/src/tint/ast/struct_member_align_attribute_test.cc
@@ -22,7 +22,7 @@
 using StructMemberAlignAttributeTest = TestHelper;
 
 TEST_F(StructMemberAlignAttributeTest, Creation) {
-    auto* d = create<StructMemberAlignAttribute>(2);
+    auto* d = create<StructMemberAlignAttribute>(2u);
     EXPECT_EQ(2u, d->align);
 }
 
diff --git a/src/tint/ast/struct_member_offset_attribute_test.cc b/src/tint/ast/struct_member_offset_attribute_test.cc
index 3c0eb41..9d81ffb 100644
--- a/src/tint/ast/struct_member_offset_attribute_test.cc
+++ b/src/tint/ast/struct_member_offset_attribute_test.cc
@@ -20,7 +20,7 @@
 using StructMemberOffsetAttributeTest = TestHelper;
 
 TEST_F(StructMemberOffsetAttributeTest, Creation) {
-    auto* d = create<StructMemberOffsetAttribute>(2);
+    auto* d = create<StructMemberOffsetAttribute>(2u);
     EXPECT_EQ(2u, d->offset);
 }
 
diff --git a/src/tint/ast/struct_member_size_attribute_test.cc b/src/tint/ast/struct_member_size_attribute_test.cc
index a9d4637..a82d53a 100644
--- a/src/tint/ast/struct_member_size_attribute_test.cc
+++ b/src/tint/ast/struct_member_size_attribute_test.cc
@@ -22,7 +22,7 @@
 using StructMemberSizeAttributeTest = TestHelper;
 
 TEST_F(StructMemberSizeAttributeTest, Creation) {
-    auto* d = create<StructMemberSizeAttribute>(2);
+    auto* d = create<StructMemberSizeAttribute>(2u);
     EXPECT_EQ(2u, d->size);
 }
 
diff --git a/src/tint/ast/var.cc b/src/tint/ast/var.cc
index 622aa03..854503e 100644
--- a/src/tint/ast/var.cc
+++ b/src/tint/ast/var.cc
@@ -36,6 +36,10 @@
 
 Var::~Var() = default;
 
+const char* Var::Kind() const {
+    return "var";
+}
+
 const Var* Var::Clone(CloneContext* ctx) const {
     auto src = ctx->Clone(source);
     auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/var.h b/src/tint/ast/var.h
index fd03580..565ebbb 100644
--- a/src/tint/ast/var.h
+++ b/src/tint/ast/var.h
@@ -65,6 +65,9 @@
     /// Destructor
     ~Var() override;
 
+    /// @returns "var"
+    const char* Kind() const override;
+
     /// Clones this node and all transitive child nodes using the `CloneContext`
     /// `ctx`.
     /// @param ctx the clone context
diff --git a/src/tint/ast/variable.h b/src/tint/ast/variable.h
index bc25753..631fbe5 100644
--- a/src/tint/ast/variable.h
+++ b/src/tint/ast/variable.h
@@ -77,6 +77,10 @@
     /// @note binding points should only be applied to Var and Parameter types.
     VariableBindingPoint BindingPoint() const;
 
+    /// @returns the kind of the variable, which can be used in diagnostics
+    ///          e.g. "var", "let", "const", etc
+    virtual const char* Kind() const = 0;
+
     /// The variable symbol
     const Symbol symbol;
 
diff --git a/src/tint/ast/variable_test.cc b/src/tint/ast/variable_test.cc
index e62ec1e..025fa6b 100644
--- a/src/tint/ast/variable_test.cc
+++ b/src/tint/ast/variable_test.cc
@@ -94,9 +94,9 @@
 TEST_F(VariableTest, WithAttributes) {
     auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, nullptr,
                     AttributeList{
-                        create<LocationAttribute>(1),
+                        create<LocationAttribute>(1u),
                         create<BuiltinAttribute>(Builtin::kPosition),
-                        create<IdAttribute>(1200),
+                        create<IdAttribute>(1200u),
                     });
 
     auto& attributes = var->attributes;
@@ -112,8 +112,8 @@
 TEST_F(VariableTest, BindingPoint) {
     auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, nullptr,
                     AttributeList{
-                        create<BindingAttribute>(2),
-                        create<GroupAttribute>(1),
+                        create<BindingAttribute>(2u),
+                        create<GroupAttribute>(1u),
                     });
     EXPECT_TRUE(var->BindingPoint());
     ASSERT_NE(var->BindingPoint().binding, nullptr);
@@ -132,7 +132,7 @@
 TEST_F(VariableTest, BindingPointMissingGroupAttribute) {
     auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, nullptr,
                     AttributeList{
-                        create<BindingAttribute>(2),
+                        create<BindingAttribute>(2u),
                     });
     EXPECT_FALSE(var->BindingPoint());
     ASSERT_NE(var->BindingPoint().binding, nullptr);
@@ -142,7 +142,7 @@
 
 TEST_F(VariableTest, BindingPointMissingBindingAttribute) {
     auto* var = Var("my_var", ty.i32(), StorageClass::kFunction, nullptr,
-                    AttributeList{create<GroupAttribute>(1)});
+                    AttributeList{create<GroupAttribute>(1u)});
     EXPECT_FALSE(var->BindingPoint());
     ASSERT_NE(var->BindingPoint().group, nullptr);
     EXPECT_EQ(var->BindingPoint().group->value, 1u);
diff --git a/src/tint/ast/vector_test.cc b/src/tint/ast/vector_test.cc
index a701852..da2ad1f 100644
--- a/src/tint/ast/vector_test.cc
+++ b/src/tint/ast/vector_test.cc
@@ -24,14 +24,14 @@
 
 TEST_F(AstVectorTest, Creation) {
     auto* i32 = create<I32>();
-    auto* v = create<Vector>(i32, 2);
+    auto* v = create<Vector>(i32, 2u);
     EXPECT_EQ(v->type, i32);
     EXPECT_EQ(v->width, 2u);
 }
 
 TEST_F(AstVectorTest, FriendlyName) {
     auto* f32 = create<F32>();
-    auto* v = create<Vector>(f32, 3);
+    auto* v = create<Vector>(f32, 3u);
     EXPECT_EQ(v->FriendlyName(Symbols()), "vec3<f32>");
 }
 
diff --git a/src/tint/castable.cc b/src/tint/castable.cc
index cff430e..40c32da 100644
--- a/src/tint/castable.cc
+++ b/src/tint/castable.cc
@@ -26,4 +26,8 @@
     tint::TypeInfo::FullHashCodeOf<CastableBase>(),
 };
 
+CastableBase::CastableBase(const CastableBase&) = default;
+
+CastableBase::~CastableBase() = default;
+
 }  // namespace tint
diff --git a/src/tint/castable.h b/src/tint/castable.h
index 048d1e5..c7b8608 100644
--- a/src/tint/castable.h
+++ b/src/tint/castable.h
@@ -320,10 +320,10 @@
 class CastableBase {
   public:
     /// Copy constructor
-    CastableBase(const CastableBase&) = default;
+    CastableBase(const CastableBase&);
 
     /// Destructor
-    virtual ~CastableBase() = default;
+    virtual ~CastableBase();
 
     /// Copy assignment
     /// @param other the CastableBase to copy
@@ -626,7 +626,7 @@
 
     // Static assertions
     static constexpr bool kDefaultIsOK =
-        kDefaultIndex == -1 || kDefaultIndex == std::tuple_size_v<Cases> - 1;
+        kDefaultIndex == -1 || kDefaultIndex == static_cast<int>(std::tuple_size_v<Cases> - 1);
     static constexpr bool kReturnIsOK =
         kHasDefaultCase || !kHasReturnType || std::is_constructible_v<RETURN_TYPE>;
     static_assert(kDefaultIsOK, "Default case must be last in Switch()");
diff --git a/src/tint/clone_context.cc b/src/tint/clone_context.cc
index 0a9e606..513c710 100644
--- a/src/tint/clone_context.cc
+++ b/src/tint/clone_context.cc
@@ -23,6 +23,10 @@
 
 namespace tint {
 
+Cloneable::Cloneable() = default;
+Cloneable::Cloneable(Cloneable&&) = default;
+Cloneable::~Cloneable() = default;
+
 CloneContext::ListTransforms::ListTransforms() = default;
 CloneContext::ListTransforms::~ListTransforms() = default;
 
diff --git a/src/tint/clone_context.h b/src/tint/clone_context.h
index 887d628..e8e197f 100644
--- a/src/tint/clone_context.h
+++ b/src/tint/clone_context.h
@@ -48,6 +48,13 @@
 /// Cloneable is the base class for all objects that can be cloned
 class Cloneable : public Castable<Cloneable> {
   public:
+    /// Constructor
+    Cloneable();
+    /// Move constructor
+    Cloneable(Cloneable&&);
+    /// Destructor
+    ~Cloneable() override;
+
     /// Performs a deep clone of this object using the CloneContext `ctx`.
     /// @param ctx the clone context
     /// @return the newly cloned object
diff --git a/src/tint/demangler.cc b/src/tint/demangler.cc
index 0116be0..d68a62a 100644
--- a/src/tint/demangler.cc
+++ b/src/tint/demangler.cc
@@ -49,7 +49,7 @@
         auto len = end_idx - start_idx;
 
         auto id = str.substr(start_idx, len);
-        Symbol sym(std::stoi(id), symbols.ProgramID());
+        Symbol sym(static_cast<uint32_t>(std::stoi(id)), symbols.ProgramID());
         out << symbols.NameFor(sym);
 
         pos = end_idx;
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_unary_operators.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_unary_operators.cc
index ed2f442..fb3fb96 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_unary_operators.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutation_finders/change_unary_operators.cc
@@ -16,6 +16,7 @@
 
 #include <memory>
 
+#include "src/tint/ast/unary_op_expression.h"
 #include "src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_operator.h"
 #include "src/tint/fuzzers/tint_ast_fuzzer/util.h"
 #include "src/tint/sem/reference.h"
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc
index d27fa7f..6e78cb5 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_binary_operator.cc
@@ -16,6 +16,8 @@
 
 #include <utility>
 
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/bool.h"
 #include "src/tint/sem/reference.h"
 
 namespace tint::fuzzers::ast_fuzzer {
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_operator.h b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_operator.h
index d80ec3f..1fa84a8 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_operator.h
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/change_unary_operator.h
@@ -15,6 +15,7 @@
 #ifndef SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_CHANGE_UNARY_OPERATOR_H_
 #define SRC_TINT_FUZZERS_TINT_AST_FUZZER_MUTATIONS_CHANGE_UNARY_OPERATOR_H_
 
+#include "src/tint/ast/unary_op.h"
 #include "src/tint/fuzzers/tint_ast_fuzzer/mutation.h"
 #include "src/tint/sem/variable.h"
 
diff --git a/src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h b/src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h
index ec28bf9..96d0926 100644
--- a/src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h
+++ b/src/tint/fuzzers/tint_ast_fuzzer/mutations/wrap_unary_operator.h
@@ -19,6 +19,7 @@
 
 #include "src/tint/fuzzers/tint_ast_fuzzer/mutation.h"
 
+#include "src/tint/ast/unary_op.h"
 #include "src/tint/sem/variable.h"
 
 namespace tint::fuzzers::ast_fuzzer {
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index df208f6..c5b3eb7 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -22,12 +22,17 @@
 #include "src/tint/ast/extension.h"
 #include "src/tint/ast/float_literal_expression.h"
 #include "src/tint/ast/id_attribute.h"
+#include "src/tint/ast/int_literal_expression.h"
 #include "src/tint/ast/interpolate_attribute.h"
 #include "src/tint/ast/location_attribute.h"
 #include "src/tint/ast/module.h"
+#include "src/tint/ast/override.h"
+#include "src/tint/ast/var.h"
 #include "src/tint/sem/array.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/depth_multisampled_texture.h"
+#include "src/tint/sem/depth_texture.h"
+#include "src/tint/sem/external_texture.h"
 #include "src/tint/sem/f16.h"
 #include "src/tint/sem/f32.h"
 #include "src/tint/sem/function.h"
@@ -810,8 +815,8 @@
             continue;
         }
 
-        auto* t = c->args[texture_index];
-        auto* s = c->args[sampler_index];
+        auto* t = c->args[static_cast<size_t>(texture_index)];
+        auto* s = c->args[static_cast<size_t>(sampler_index)];
 
         GetOriginatingResources(
             std::array<const ast::Expression*, 2>{t, s},
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index fb09522..4e369d5 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -3048,7 +3048,7 @@
     // here the struct is expected to occupy 1024 bytes of workgroup storage.
     const auto* wg_struct_type = MakeStructTypeFromMembers(
         "WgStruct",
-        {MakeStructMember(0, ty.f32(), {create<ast::StructMemberAlignAttribute>(1024)})});
+        {MakeStructMember(0, ty.f32(), {create<ast::StructMemberAlignAttribute>(1024u)})});
 
     AddWorkgroupStorage("wg_struct_var", ty.Of(wg_struct_type));
     MakeStructVariableReferenceBodyFunction("wg_struct_func", "wg_struct_var", {{0, ty.f32()}});
diff --git a/src/tint/inspector/test_inspector_builder.cc b/src/tint/inspector/test_inspector_builder.cc
index 949ee75..23b473c 100644
--- a/src/tint/inspector/test_inspector_builder.cc
+++ b/src/tint/inspector/test_inspector_builder.cc
@@ -121,15 +121,15 @@
                                         const ast::Type* type,
                                         uint32_t group,
                                         uint32_t binding) {
-    Global(name, type, ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(binding),
-               create<ast::GroupAttribute>(group),
-           });
+    GlobalVar(name, type, ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(binding),
+                  create<ast::GroupAttribute>(group),
+              });
 }
 
 void InspectorBuilder::AddWorkgroupStorage(const std::string& name, const ast::Type* type) {
-    Global(name, type, ast::StorageClass::kWorkgroup);
+    GlobalVar(name, type, ast::StorageClass::kWorkgroup);
 }
 
 void InspectorBuilder::AddStorageBuffer(const std::string& name,
@@ -137,11 +137,11 @@
                                         ast::Access access,
                                         uint32_t group,
                                         uint32_t binding) {
-    Global(name, type, ast::StorageClass::kStorage, access,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(binding),
-               create<ast::GroupAttribute>(group),
-           });
+    GlobalVar(name, type, ast::StorageClass::kStorage, access,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(binding),
+                  create<ast::GroupAttribute>(group),
+              });
 }
 
 void InspectorBuilder::MakeStructVariableReferenceBodyFunction(
@@ -173,36 +173,36 @@
 }
 
 void InspectorBuilder::AddSampler(const std::string& name, uint32_t group, uint32_t binding) {
-    Global(name, sampler_type(),
-           ast::AttributeList{
-               create<ast::BindingAttribute>(binding),
-               create<ast::GroupAttribute>(group),
-           });
+    GlobalVar(name, sampler_type(),
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(binding),
+                  create<ast::GroupAttribute>(group),
+              });
 }
 
 void InspectorBuilder::AddComparisonSampler(const std::string& name,
                                             uint32_t group,
                                             uint32_t binding) {
-    Global(name, comparison_sampler_type(),
-           ast::AttributeList{
-               create<ast::BindingAttribute>(binding),
-               create<ast::GroupAttribute>(group),
-           });
+    GlobalVar(name, comparison_sampler_type(),
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(binding),
+                  create<ast::GroupAttribute>(group),
+              });
 }
 
 void InspectorBuilder::AddResource(const std::string& name,
                                    const ast::Type* type,
                                    uint32_t group,
                                    uint32_t binding) {
-    Global(name, type,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(binding),
-               create<ast::GroupAttribute>(group),
-           });
+    GlobalVar(name, type,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(binding),
+                  create<ast::GroupAttribute>(group),
+              });
 }
 
 void InspectorBuilder::AddGlobalVariable(const std::string& name, const ast::Type* type) {
-    Global(name, type, ast::StorageClass::kPrivate);
+    GlobalVar(name, type, ast::StorageClass::kPrivate);
 }
 
 const ast::Function* InspectorBuilder::MakeSamplerReferenceBodyFunction(
@@ -285,11 +285,11 @@
             return scalar;
         case ast::TextureDimension::k2d:
         case ast::TextureDimension::k2dArray:
-            return create<ast::Vector>(scalar, 2);
+            return create<ast::Vector>(scalar, 2u);
         case ast::TextureDimension::k3d:
         case ast::TextureDimension::kCube:
         case ast::TextureDimension::kCubeArray:
-            return create<ast::Vector>(scalar, 3);
+            return create<ast::Vector>(scalar, 3u);
         default:
             [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
     }
@@ -305,11 +305,11 @@
                                          const ast::Type* type,
                                          uint32_t group,
                                          uint32_t binding) {
-    Global(name, type,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(binding),
-               create<ast::GroupAttribute>(group),
-           });
+    GlobalVar(name, type,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(binding),
+                  create<ast::GroupAttribute>(group),
+              });
 }
 
 const ast::Function* InspectorBuilder::MakeStorageTextureBodyFunction(
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index f74639d..17ce925 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -71,11 +71,12 @@
 
 // https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
 type bool
-@precedence(4) @display("abstract-float") type af
-@precedence(3) @display("abstract-int")   type ai
-@precedence(2) type i32
-@precedence(1) type u32
-@precedence(0) type f32
+@precedence(5) @display("abstract-float") type af
+@precedence(4) @display("abstract-int")   type ai
+@precedence(3) type i32
+@precedence(2) type u32
+@precedence(1) type f32
+@precedence(0) type f16
 type vec2<T>
 type vec3<T>
 type vec4<T>
@@ -126,16 +127,19 @@
 // A type matcher that can match one or more types.                           //
 ////////////////////////////////////////////////////////////////////////////////
 
+match f32f16: f32 | f16
 match fiu32: f32 | i32 | u32
 match fi32: f32 | i32
 match iu32: i32 | u32
-match scalar: f32 | i32 | u32 | bool
-match abstract_or_scalar: ai | af | f32 | i32 | u32 | bool
+match scalar: f32 | f16 | i32 | u32 | bool
+match abstract_or_scalar: ai | af | f32 | f16 | i32 | u32 | bool
 match af_f32: af | f32
-match scalar_no_f32: i32 | u32 | bool
-match scalar_no_i32: f32 | u32 | bool
-match scalar_no_u32: f32 | i32 | bool
-match scalar_no_bool: f32 | i32 | u32
+match af_f32f16: af | f32 | f16
+match scalar_no_f32: i32 | f16 | u32 | bool
+match scalar_no_f16: f32 | i32 | u32 | bool
+match scalar_no_i32: f32 | f16 | u32 | bool
+match scalar_no_u32: f32 | f16 | i32 | bool
+match scalar_no_bool: f32 | f16 | i32 | u32
 
 ////////////////////////////////////////////////////////////////////////////////
 // Enum matchers                                                              //
@@ -329,6 +333,8 @@
 fn abs<N: num, T: fiu32>(vec<N, T>) -> vec<N, T>
 fn acos(f32) -> f32
 fn acos<N: num>(vec<N, f32>) -> vec<N, f32>
+fn acosh(f32) -> f32
+fn acosh<N: num>(vec<N, f32>) -> vec<N, f32>
 fn all(bool) -> bool
 fn all<N: num>(vec<N, bool>) -> bool
 fn any(bool) -> bool
@@ -336,10 +342,14 @@
 fn arrayLength<T, A: access>(ptr<storage, array<T>, A>) -> u32
 fn asin(f32) -> f32
 fn asin<N: num>(vec<N, f32>) -> vec<N, f32>
+fn asinh(f32) -> f32
+fn asinh<N: num>(vec<N, f32>) -> vec<N, f32>
 fn atan(f32) -> f32
 fn atan<N: num>(vec<N, f32>) -> vec<N, f32>
 fn atan2(f32, f32) -> f32
 fn atan2<N: num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32>
+fn atanh(f32) -> f32
+fn atanh<N: num>(vec<N, f32>) -> vec<N, f32>
 fn ceil(f32) -> f32
 fn ceil<N: num>(vec<N, f32>) -> vec<N, f32>
 fn clamp<T: fiu32>(T, T, T) -> T
@@ -437,9 +447,9 @@
 fn reverseBits<N: num, T: iu32>(vec<N, T>) -> vec<N, T>
 fn round(f32) -> f32
 fn round<N: num>(vec<N, f32>) -> vec<N, f32>
-fn select<T: scalar>(T, T, bool) -> T
-fn select<T: scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
-fn select<N: num, T: scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
+fn select<T: scalar_no_f16>(T, T, bool) -> T
+fn select<T: scalar_no_f16, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
+fn select<N: num, T: scalar_no_f16>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
 fn sign(f32) -> f32
 fn sign<N: num>(vec<N, f32>) -> vec<N, f32>
 fn sin(f32) -> f32
@@ -629,37 +639,39 @@
 ctor i32() -> i32
 ctor u32() -> u32
 ctor f32() -> f32
+ctor f16() -> f16
 ctor bool() -> bool
 ctor vec2<T: scalar>() -> vec2<T>
 ctor vec3<T: scalar>() -> vec3<T>
 ctor vec4<T: scalar>() -> vec4<T>
-ctor mat2x2() -> mat2x2<f32>
-ctor mat2x3() -> mat2x3<f32>
-ctor mat2x4() -> mat2x4<f32>
-ctor mat3x2() -> mat3x2<f32>
-ctor mat3x3() -> mat3x3<f32>
-ctor mat3x4() -> mat3x4<f32>
-ctor mat4x2() -> mat4x2<f32>
-ctor mat4x3() -> mat4x3<f32>
-ctor mat4x4() -> mat4x4<f32>
+ctor mat2x2<T: f32f16>() -> mat2x2<T>
+ctor mat2x3<T: f32f16>() -> mat2x3<T>
+ctor mat2x4<T: f32f16>() -> mat2x4<T>
+ctor mat3x2<T: f32f16>() -> mat3x2<T>
+ctor mat3x3<T: f32f16>() -> mat3x3<T>
+ctor mat3x4<T: f32f16>() -> mat3x4<T>
+ctor mat4x2<T: f32f16>() -> mat4x2<T>
+ctor mat4x3<T: f32f16>() -> mat4x3<T>
+ctor mat4x4<T: f32f16>() -> mat4x4<T>
 
 // Identity constructors
 ctor i32(i32) -> i32
 ctor u32(u32) -> u32
 ctor f32(f32) -> f32
+ctor f16(f16) -> f16
 ctor bool(bool) -> bool
 ctor vec2<T: scalar>(vec2<T>) -> vec2<T>
 ctor vec3<T: scalar>(vec3<T>) -> vec3<T>
 ctor vec4<T: scalar>(vec4<T>) -> vec4<T>
-ctor mat2x2<f32>(mat2x2<f32>) -> mat2x2<f32>
-ctor mat2x3<f32>(mat2x3<f32>) -> mat2x3<f32>
-ctor mat2x4<f32>(mat2x4<f32>) -> mat2x4<f32>
-ctor mat3x2<f32>(mat3x2<f32>) -> mat3x2<f32>
-ctor mat3x3<f32>(mat3x3<f32>) -> mat3x3<f32>
-ctor mat3x4<f32>(mat3x4<f32>) -> mat3x4<f32>
-ctor mat4x2<f32>(mat4x2<f32>) -> mat4x2<f32>
-ctor mat4x3<f32>(mat4x3<f32>) -> mat4x3<f32>
-ctor mat4x4<f32>(mat4x4<f32>) -> mat4x4<f32>
+ctor mat2x2<T: f32f16>(mat2x2<T>) -> mat2x2<T>
+ctor mat2x3<T: f32f16>(mat2x3<T>) -> mat2x3<T>
+ctor mat2x4<T: f32f16>(mat2x4<T>) -> mat2x4<T>
+ctor mat3x2<T: f32f16>(mat3x2<T>) -> mat3x2<T>
+ctor mat3x3<T: f32f16>(mat3x3<T>) -> mat3x3<T>
+ctor mat3x4<T: f32f16>(mat3x4<T>) -> mat3x4<T>
+ctor mat4x2<T: f32f16>(mat4x2<T>) -> mat4x2<T>
+ctor mat4x3<T: f32f16>(mat4x3<T>) -> mat4x3<T>
+ctor mat4x4<T: f32f16>(mat4x4<T>) -> mat4x4<T>
 
 // Vector constructors
 ctor vec2<T: abstract_or_scalar>(T) -> vec2<T>
@@ -679,82 +691,106 @@
 
 // Matrix constructors
 ctor mat2x2<T: af_f32>(T) -> mat2x2<T>
-ctor mat2x2<T: af_f32>(T, T,
-                       T, T) -> mat2x2<T>
-ctor mat2x2<T: af_f32>(vec2<T>, vec2<T>) -> mat2x2<T>
-
 ctor mat2x3<T: af_f32>(T) -> mat2x3<T>
-ctor mat2x3<T: af_f32>(T, T, T,
-                       T, T, T) -> mat2x3<T>
-ctor mat2x3<T: af_f32>(vec3<T>, vec3<T>) -> mat2x3<T>
-
 ctor mat2x4<T: af_f32>(T) -> mat2x4<T>
-ctor mat2x4<T: af_f32>(T, T, T, T,
-                       T, T, T, T) -> mat2x4<T>
-ctor mat2x4<T: af_f32>(vec4<T>, vec4<T>) -> mat2x4<T>
-
 ctor mat3x2<T: af_f32>(T) -> mat3x2<T>
-ctor mat3x2<T: af_f32>(T, T,
-                       T, T,
-                       T, T) -> mat3x2<T>
-ctor mat3x2<T: af_f32>(vec2<T>, vec2<T>, vec2<T>) -> mat3x2<T>
-
 ctor mat3x3<T: af_f32>(T) -> mat3x3<T>
-ctor mat3x3<T: af_f32>(T, T, T,
-                       T, T, T,
-                       T, T, T) -> mat3x3<T>
-ctor mat3x3<T: af_f32>(vec3<T>, vec3<T>, vec3<T>) -> mat3x3<T>
-
 ctor mat3x4<T: af_f32>(T) -> mat3x4<T>
-ctor mat3x4<T: af_f32>(T, T, T, T,
-                       T, T, T, T,
-                       T, T, T, T) -> mat3x4<T>
-ctor mat3x4<T: af_f32>(vec4<T>, vec4<T>, vec4<T>) -> mat3x4<T>
-
 ctor mat4x2<T: af_f32>(T) -> mat4x2<T>
-ctor mat4x2<T: af_f32>(T, T,
-                       T, T,
-                       T, T,
-                       T, T) -> mat4x2<T>
-ctor mat4x2<T: af_f32>(vec2<T>, vec2<T>, vec2<T>, vec2<T>) -> mat4x2<T>
-
 ctor mat4x3<T: af_f32>(T) -> mat4x3<T>
-ctor mat4x3<T: af_f32>(T, T, T,
-                       T, T, T,
-                       T, T, T,
-                       T, T, T) -> mat4x3<T>
-ctor mat4x3<T: af_f32>(vec3<T>, vec3<T>, vec3<T>, vec3<T>) -> mat4x3<T>
-
 ctor mat4x4<T: af_f32>(T) -> mat4x4<T>
-ctor mat4x4<T: af_f32>(T, T, T, T,
-                       T, T, T, T,
-                       T, T, T, T,
-                       T, T, T, T) -> mat4x4<T>
-ctor mat4x4<T: af_f32>(vec4<T>, vec4<T>, vec4<T>, vec4<T>) -> mat4x4<T>
+
+ctor mat2x2<T: af_f32f16>(T, T,
+                          T, T) -> mat2x2<T>
+ctor mat2x2<T: af_f32f16>(vec2<T>, vec2<T>) -> mat2x2<T>
+
+ctor mat2x3<T: af_f32f16>(T, T, T,
+                          T, T, T) -> mat2x3<T>
+ctor mat2x3<T: af_f32f16>(vec3<T>, vec3<T>) -> mat2x3<T>
+
+ctor mat2x4<T: af_f32f16>(T, T, T, T,
+                          T, T, T, T) -> mat2x4<T>
+ctor mat2x4<T: af_f32f16>(vec4<T>, vec4<T>) -> mat2x4<T>
+
+ctor mat3x2<T: af_f32f16>(T, T,
+                          T, T,
+                          T, T) -> mat3x2<T>
+ctor mat3x2<T: af_f32f16>(vec2<T>, vec2<T>, vec2<T>) -> mat3x2<T>
+
+ctor mat3x3<T: af_f32f16>(T, T, T,
+                          T, T, T,
+                          T, T, T) -> mat3x3<T>
+ctor mat3x3<T: af_f32f16>(vec3<T>, vec3<T>, vec3<T>) -> mat3x3<T>
+
+ctor mat3x4<T: af_f32f16>(T, T, T, T,
+                          T, T, T, T,
+                          T, T, T, T) -> mat3x4<T>
+ctor mat3x4<T: af_f32f16>(vec4<T>, vec4<T>, vec4<T>) -> mat3x4<T>
+
+ctor mat4x2<T: af_f32f16>(T, T,
+                          T, T,
+                          T, T,
+                          T, T) -> mat4x2<T>
+ctor mat4x2<T: af_f32f16>(vec2<T>, vec2<T>, vec2<T>, vec2<T>) -> mat4x2<T>
+
+ctor mat4x3<T: af_f32f16>(T, T, T,
+                          T, T, T,
+                          T, T, T,
+                          T, T, T) -> mat4x3<T>
+ctor mat4x3<T: af_f32f16>(vec3<T>, vec3<T>, vec3<T>, vec3<T>) -> mat4x3<T>
+
+ctor mat4x4<T: af_f32f16>(T, T, T, T,
+                          T, T, T, T,
+                          T, T, T, T,
+                          T, T, T, T) -> mat4x4<T>
+ctor mat4x4<T: af_f32f16>(vec4<T>, vec4<T>, vec4<T>, vec4<T>) -> mat4x4<T>
 
 ////////////////////////////////////////////////////////////////////////////////
 // Type conversions                                                           //
 ////////////////////////////////////////////////////////////////////////////////
 conv f32<T: scalar_no_f32>(T) -> f32
+conv f16<T: scalar_no_f16>(T) -> f16
 conv i32<T: scalar_no_i32>(T) -> i32
 conv u32<T: scalar_no_u32>(T) -> u32
 conv bool<T: scalar_no_bool>(T) -> bool
 
 conv vec2<T: f32, U: scalar_no_f32>(vec2<U>) -> vec2<f32>
+conv vec2<T: f16, U: scalar_no_f16>(vec2<U>) -> vec2<f16>
 conv vec2<T: i32, U: scalar_no_i32>(vec2<U>) -> vec2<i32>
 conv vec2<T: u32, U: scalar_no_u32>(vec2<U>) -> vec2<u32>
 conv vec2<T: bool, U: scalar_no_bool>(vec2<U>) -> vec2<bool>
 
 conv vec3<T: f32, U: scalar_no_f32>(vec3<U>) -> vec3<f32>
+conv vec3<T: f16, U: scalar_no_f16>(vec3<U>) -> vec3<f16>
 conv vec3<T: i32, U: scalar_no_i32>(vec3<U>) -> vec3<i32>
 conv vec3<T: u32, U: scalar_no_u32>(vec3<U>) -> vec3<u32>
 conv vec3<T: bool, U: scalar_no_bool>(vec3<U>) -> vec3<bool>
 
 conv vec4<T: f32, U: scalar_no_f32>(vec4<U>) -> vec4<f32>
+conv vec4<T: f16, U: scalar_no_f16>(vec4<U>) -> vec4<f16>
 conv vec4<T: i32, U: scalar_no_i32>(vec4<U>) -> vec4<i32>
 conv vec4<T: u32, U: scalar_no_u32>(vec4<U>) -> vec4<u32>
 conv vec4<T: bool, U: scalar_no_bool>(vec4<U>) -> vec4<bool>
 
+conv mat2x2<T: f16>(mat2x2<f32>) -> mat2x2<f16>
+conv mat2x2<T: f32>(mat2x2<f16>) -> mat2x2<f32>
+conv mat2x3<T: f16>(mat2x3<f32>) -> mat2x3<f16>
+conv mat2x3<T: f32>(mat2x3<f16>) -> mat2x3<f32>
+conv mat2x4<T: f16>(mat2x4<f32>) -> mat2x4<f16>
+conv mat2x4<T: f32>(mat2x4<f16>) -> mat2x4<f32>
+conv mat3x2<T: f16>(mat3x2<f32>) -> mat3x2<f16>
+conv mat3x2<T: f32>(mat3x2<f16>) -> mat3x2<f32>
+conv mat3x3<T: f16>(mat3x3<f32>) -> mat3x3<f16>
+conv mat3x3<T: f32>(mat3x3<f16>) -> mat3x3<f32>
+conv mat3x4<T: f16>(mat3x4<f32>) -> mat3x4<f16>
+conv mat3x4<T: f32>(mat3x4<f16>) -> mat3x4<f32>
+conv mat4x2<T: f16>(mat4x2<f32>) -> mat4x2<f16>
+conv mat4x2<T: f32>(mat4x2<f16>) -> mat4x2<f32>
+conv mat4x3<T: f16>(mat4x3<f32>) -> mat4x3<f16>
+conv mat4x3<T: f32>(mat4x3<f16>) -> mat4x3<f32>
+conv mat4x4<T: f16>(mat4x4<f32>) -> mat4x4<f16>
+conv mat4x4<T: f32>(mat4x4<f16>) -> mat4x4<f32>
+
 ////////////////////////////////////////////////////////////////////////////////
 // Operators                                                                  //
 //                                                                            //
diff --git a/src/tint/number.h b/src/tint/number.h
index 7f0c2f1..e130019 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -16,6 +16,7 @@
 #define SRC_TINT_NUMBER_H_
 
 #include <stdint.h>
+#include <cmath>
 #include <functional>
 #include <limits>
 #include <optional>
@@ -135,61 +136,6 @@
     return out << num.value;
 }
 
-/// Equality operator.
-/// @param a the LHS number
-/// @param b the RHS number
-/// @returns true if the numbers `a` and `b` are exactly equal.
-template <typename A, typename B>
-bool operator==(Number<A> a, Number<B> b) {
-    using T = decltype(a.value + b.value);
-    return std::equal_to<T>()(static_cast<T>(a.value), static_cast<T>(b.value));
-}
-
-/// Inequality operator.
-/// @param a the LHS number
-/// @param b the RHS number
-/// @returns true if the numbers `a` and `b` are exactly unequal.
-template <typename A, typename B>
-bool operator!=(Number<A> a, Number<B> b) {
-    return !(a == b);
-}
-
-/// Equality operator.
-/// @param a the LHS number
-/// @param b the RHS number
-/// @returns true if the numbers `a` and `b` are exactly equal.
-template <typename A, typename B>
-std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) {
-    return a == Number<B>(b);
-}
-
-/// Inequality operator.
-/// @param a the LHS number
-/// @param b the RHS number
-/// @returns true if the numbers `a` and `b` are exactly unequal.
-template <typename A, typename B>
-std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
-    return !(a == b);
-}
-
-/// Equality operator.
-/// @param a the LHS number
-/// @param b the RHS number
-/// @returns true if the numbers `a` and `b` are exactly equal.
-template <typename A, typename B>
-std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) {
-    return Number<A>(a) == b;
-}
-
-/// Inequality operator.
-/// @param a the LHS number
-/// @param b the RHS number
-/// @returns true if the numbers `a` and `b` are exactly unequal.
-template <typename A, typename B>
-std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
-    return !(a == b);
-}
-
 /// The partial specification of Number for f16 type, storing the f16 value as float,
 /// and enforcing proper explicit casting.
 template <>
@@ -282,7 +228,9 @@
 /// @returns the resulting value of the conversion, or a failure reason.
 template <typename TO, typename FROM>
 utils::Result<TO, ConversionFailure> CheckedConvert(Number<FROM> num) {
-    using T = decltype(UnwrapNumber<TO>() + num.value);
+    // Use the highest-precision integer or floating-point type to perform the comparisons.
+    using T = std::conditional_t<IsFloatingPoint<UnwrapNumber<TO>> || IsFloatingPoint<FROM>,
+                                 AFloat::type, AInt::type>;
     const auto value = static_cast<T>(num.value);
     if (value > static_cast<T>(TO::kHighest)) {
         return ConversionFailure::kExceedsPositiveLimit;
@@ -293,6 +241,70 @@
     return TO(value);  // Success
 }
 
+/// Equality operator.
+/// @param a the LHS number
+/// @param b the RHS number
+/// @returns true if the numbers `a` and `b` are exactly equal. Also considers sign bit.
+template <typename A, typename B>
+bool operator==(Number<A> a, Number<B> b) {
+    // Use the highest-precision integer or floating-point type to perform the comparisons.
+    using T =
+        std::conditional_t<IsFloatingPoint<A> || IsFloatingPoint<B>, AFloat::type, AInt::type>;
+    auto va = static_cast<T>(a.value);
+    auto vb = static_cast<T>(b.value);
+    if constexpr (IsFloatingPoint<T>) {
+        if (std::signbit(va) != std::signbit(vb)) {
+            return false;
+        }
+    }
+    return std::equal_to<T>()(va, vb);
+}
+
+/// Inequality operator.
+/// @param a the LHS number
+/// @param b the RHS number
+/// @returns true if the numbers `a` and `b` are exactly unequal. Also considers sign bit.
+template <typename A, typename B>
+bool operator!=(Number<A> a, Number<B> b) {
+    return !(a == b);
+}
+
+/// Equality operator.
+/// @param a the LHS number
+/// @param b the RHS number
+/// @returns true if the numbers `a` and `b` are exactly equal.
+template <typename A, typename B>
+std::enable_if_t<IsNumeric<B>, bool> operator==(Number<A> a, B b) {
+    return a == Number<B>(b);
+}
+
+/// Inequality operator.
+/// @param a the LHS number
+/// @param b the RHS number
+/// @returns true if the numbers `a` and `b` are exactly unequal.
+template <typename A, typename B>
+std::enable_if_t<IsNumeric<B>, bool> operator!=(Number<A> a, B b) {
+    return !(a == b);
+}
+
+/// Equality operator.
+/// @param a the LHS number
+/// @param b the RHS number
+/// @returns true if the numbers `a` and `b` are exactly equal.
+template <typename A, typename B>
+std::enable_if_t<IsNumeric<A>, bool> operator==(A a, Number<B> b) {
+    return Number<A>(a) == b;
+}
+
+/// Inequality operator.
+/// @param a the LHS number
+/// @param b the RHS number
+/// @returns true if the numbers `a` and `b` are exactly unequal.
+template <typename A, typename B>
+std::enable_if_t<IsNumeric<A>, bool> operator!=(A a, Number<B> b) {
+    return !(a == b);
+}
+
 /// Define 'TINT_HAS_OVERFLOW_BUILTINS' if the compiler provide overflow checking builtins.
 /// If the compiler does not support these builtins, then these are emulated with algorithms
 /// described in:
diff --git a/src/tint/number_test.cc b/src/tint/number_test.cc
index 52ba4ae..81acc04 100644
--- a/src/tint/number_test.cc
+++ b/src/tint/number_test.cc
@@ -65,6 +65,79 @@
 // warning.
 TINT_BEGIN_DISABLE_WARNING(CONSTANT_OVERFLOW);
 
+TEST(NumberTest, Equality) {
+    EXPECT_TRUE(0_a == 0_a);
+    EXPECT_TRUE(10_a == 10_a);
+    EXPECT_TRUE(-10_a == -10_a);
+
+    EXPECT_TRUE(0_i == 0_i);
+    EXPECT_TRUE(10_i == 10_i);
+    EXPECT_TRUE(-10_i == -10_i);
+
+    EXPECT_TRUE(0_u == 0_u);
+    EXPECT_TRUE(10_u == 10_u);
+
+    EXPECT_TRUE(0._a == 0._a);
+    EXPECT_TRUE(-0._a == -0._a);
+    EXPECT_TRUE(10._a == 10._a);
+    EXPECT_TRUE(-10._a == -10._a);
+
+    EXPECT_TRUE(0_f == 0_f);
+    EXPECT_TRUE(-0_f == -0_f);
+    EXPECT_TRUE(10_f == 10_f);
+    EXPECT_TRUE(-10_f == -10_f);
+
+    EXPECT_TRUE(0_h == 0_h);
+    EXPECT_TRUE(-0_h == -0_h);
+    EXPECT_TRUE(10_h == 10_h);
+    EXPECT_TRUE(-10_h == -10_h);
+}
+
+TEST(NumberTest, Inequality) {
+    EXPECT_TRUE(0_a != 1_a);
+    EXPECT_TRUE(10_a != 11_a);
+    EXPECT_TRUE(11_a != 10_a);
+    EXPECT_TRUE(-10_a != -11_a);
+    EXPECT_TRUE(-11_a != -10_a);
+
+    EXPECT_TRUE(0_i != 1_i);
+    EXPECT_TRUE(1_i != 0_i);
+    EXPECT_TRUE(10_i != 11_i);
+    EXPECT_TRUE(11_i != 10_i);
+    EXPECT_TRUE(-10_i != -11_i);
+    EXPECT_TRUE(-11_i != -10_i);
+
+    EXPECT_TRUE(0_u != 1_u);
+    EXPECT_TRUE(1_u != 0_u);
+    EXPECT_TRUE(10_u != 11_u);
+    EXPECT_TRUE(11_u != 10_u);
+
+    EXPECT_TRUE(0._a != -0._a);
+    EXPECT_TRUE(-0._a != 0._a);
+    EXPECT_TRUE(10._a != 11._a);
+    EXPECT_TRUE(11._a != 10._a);
+    EXPECT_TRUE(-10._a != -11._a);
+    EXPECT_TRUE(-11._a != -10._a);
+
+    EXPECT_TRUE(0_f != -0_f);
+    EXPECT_TRUE(-0_f != 0_f);
+    EXPECT_TRUE(-0_f != -1_f);
+    EXPECT_TRUE(-1_f != -0_f);
+    EXPECT_TRUE(10_f != -10_f);
+    EXPECT_TRUE(-10_f != 10_f);
+    EXPECT_TRUE(10_f != 11_f);
+    EXPECT_TRUE(-10_f != -11_f);
+
+    EXPECT_TRUE(0_h != -0_h);
+    EXPECT_TRUE(-0_h != 0_h);
+    EXPECT_TRUE(-0_h != -1_h);
+    EXPECT_TRUE(-1_h != -0_h);
+    EXPECT_TRUE(10_h != -10_h);
+    EXPECT_TRUE(-10_h != 10_h);
+    EXPECT_TRUE(10_h != 11_h);
+    EXPECT_TRUE(-10_h != -11_h);
+}
+
 TEST(NumberTest, CheckedConvertIdentity) {
     EXPECT_EQ(CheckedConvert<AInt>(0_a), 0_a);
     EXPECT_EQ(CheckedConvert<AFloat>(0_a), 0.0_a);
@@ -84,6 +157,7 @@
 TEST(NumberTest, CheckedConvertLargestValue) {
     EXPECT_EQ(CheckedConvert<i32>(AInt(kHighestI32)), i32(kHighestI32));
     EXPECT_EQ(CheckedConvert<u32>(AInt(kHighestU32)), u32(kHighestU32));
+    EXPECT_EQ(CheckedConvert<u32>(i32(kHighestI32)), u32(kHighestI32));
     EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32)), f32(kHighestF32));
     EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16)), f16(kHighestF16));
 }
@@ -105,6 +179,14 @@
 TEST(NumberTest, CheckedConvertExceedsPositiveLimit) {
     EXPECT_EQ(CheckedConvert<i32>(AInt(kHighestI32 + 1)), ConversionFailure::kExceedsPositiveLimit);
     EXPECT_EQ(CheckedConvert<u32>(AInt(kHighestU32 + 1)), ConversionFailure::kExceedsPositiveLimit);
+    EXPECT_EQ(CheckedConvert<i32>(u32(kHighestU32)), ConversionFailure::kExceedsPositiveLimit);
+    EXPECT_EQ(CheckedConvert<i32>(u32(0x80000000)), ConversionFailure::kExceedsPositiveLimit);
+    EXPECT_EQ(CheckedConvert<u32>(f32(f32::kHighest)), ConversionFailure::kExceedsPositiveLimit);
+    EXPECT_EQ(CheckedConvert<i32>(f32(f32::kHighest)), ConversionFailure::kExceedsPositiveLimit);
+    EXPECT_EQ(CheckedConvert<u32>(AFloat(AFloat::kHighest)),
+              ConversionFailure::kExceedsPositiveLimit);
+    EXPECT_EQ(CheckedConvert<i32>(AFloat(AFloat::kHighest)),
+              ConversionFailure::kExceedsPositiveLimit);
     EXPECT_EQ(CheckedConvert<f32>(AFloat(kHighestF32NextULP)),
               ConversionFailure::kExceedsPositiveLimit);
     EXPECT_EQ(CheckedConvert<f16>(AFloat(kHighestF16NextULP)),
@@ -114,6 +196,14 @@
 TEST(NumberTest, CheckedConvertExceedsNegativeLimit) {
     EXPECT_EQ(CheckedConvert<i32>(AInt(kLowestI32 - 1)), ConversionFailure::kExceedsNegativeLimit);
     EXPECT_EQ(CheckedConvert<u32>(AInt(kLowestU32 - 1)), ConversionFailure::kExceedsNegativeLimit);
+    EXPECT_EQ(CheckedConvert<u32>(i32(-1)), ConversionFailure::kExceedsNegativeLimit);
+    EXPECT_EQ(CheckedConvert<u32>(i32(kLowestI32)), ConversionFailure::kExceedsNegativeLimit);
+    EXPECT_EQ(CheckedConvert<u32>(f32(f32::kLowest)), ConversionFailure::kExceedsNegativeLimit);
+    EXPECT_EQ(CheckedConvert<i32>(f32(f32::kLowest)), ConversionFailure::kExceedsNegativeLimit);
+    EXPECT_EQ(CheckedConvert<u32>(AFloat(AFloat::kLowest)),
+              ConversionFailure::kExceedsNegativeLimit);
+    EXPECT_EQ(CheckedConvert<i32>(AFloat(AFloat::kLowest)),
+              ConversionFailure::kExceedsNegativeLimit);
     EXPECT_EQ(CheckedConvert<f32>(AFloat(kLowestF32NextULP)),
               ConversionFailure::kExceedsNegativeLimit);
     EXPECT_EQ(CheckedConvert<f16>(AFloat(kLowestF16NextULP)),
@@ -194,7 +284,7 @@
     EXPECT_EQ(f16(lowestNegativeSubnormalF16PlusULP), -0x0.ff8p-14);
     EXPECT_EQ(f16(highestNegativeSubnormalF16MinusULP), highestNegativeSubnormalF16);
     EXPECT_EQ(f16(highestNegativeSubnormalF16), highestNegativeSubnormalF16);
-    EXPECT_EQ(f16(highestNegativeSubnormalF16PlusULP), 0.0);
+    EXPECT_EQ(f16(highestNegativeSubnormalF16PlusULP), -0.0);
     // Test the mantissa discarding.
     EXPECT_EQ(f16(-0x0.064p-14), -0x0.064p-14);
     EXPECT_EQ(f16(-0x0.067fecp-14), -0x0.064p-14);
diff --git a/src/tint/program.cc b/src/tint/program.cc
index 6722a09..1afbd46 100644
--- a/src/tint/program.cc
+++ b/src/tint/program.cc
@@ -38,6 +38,7 @@
       types_(std::move(program.types_)),
       ast_nodes_(std::move(program.ast_nodes_)),
       sem_nodes_(std::move(program.sem_nodes_)),
+      constant_nodes_(std::move(program.constant_nodes_)),
       ast_(std::move(program.ast_)),
       sem_(std::move(program.sem_)),
       symbols_(std::move(program.symbols_)),
@@ -62,6 +63,7 @@
     types_ = std::move(builder.Types());
     ast_nodes_ = std::move(builder.ASTNodes());
     sem_nodes_ = std::move(builder.SemNodes());
+    constant_nodes_ = std::move(builder.ConstantNodes());
     ast_ = &builder.AST();  // ast::Module is actually a heap allocation.
     sem_ = std::move(builder.Sem());
     symbols_ = std::move(builder.Symbols());
@@ -86,6 +88,7 @@
     types_ = std::move(program.types_);
     ast_nodes_ = std::move(program.ast_nodes_);
     sem_nodes_ = std::move(program.sem_nodes_);
+    constant_nodes_ = std::move(program.constant_nodes_);
     ast_ = std::move(program.ast_);
     sem_ = std::move(program.sem_);
     symbols_ = std::move(program.symbols_);
diff --git a/src/tint/program.h b/src/tint/program.h
index 3230e7e..5fd31dd 100644
--- a/src/tint/program.h
+++ b/src/tint/program.h
@@ -20,6 +20,7 @@
 
 #include "src/tint/ast/function.h"
 #include "src/tint/program_id.h"
+#include "src/tint/sem/constant.h"
 #include "src/tint/sem/info.h"
 #include "src/tint/sem/type_manager.h"
 #include "src/tint/symbol_table.h"
@@ -43,6 +44,9 @@
     /// SemNodeAllocator is an alias to BlockAllocator<sem::Node>
     using SemNodeAllocator = utils::BlockAllocator<sem::Node>;
 
+    /// ConstantAllocator is an alias to BlockAllocator<sem::Constant>
+    using ConstantAllocator = utils::BlockAllocator<sem::Constant>;
+
     /// Constructor
     Program();
 
@@ -160,6 +164,7 @@
     sem::Manager types_;
     ASTNodeAllocator ast_nodes_;
     SemNodeAllocator sem_nodes_;
+    ConstantAllocator constant_nodes_;
     ast::Module* ast_ = nullptr;
     sem::Info sem_;
     SymbolTable symbols_{id_};
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 5492867..e9b0a80 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -33,6 +33,7 @@
 #include "src/tint/ast/call_statement.h"
 #include "src/tint/ast/case_statement.h"
 #include "src/tint/ast/compound_assignment_statement.h"
+#include "src/tint/ast/const.h"
 #include "src/tint/ast/continue_statement.h"
 #include "src/tint/ast/depth_multisampled_texture.h"
 #include "src/tint/ast/depth_texture.h"
@@ -87,6 +88,7 @@
 #include "src/tint/program_id.h"
 #include "src/tint/sem/array.h"
 #include "src/tint/sem/bool.h"
+#include "src/tint/sem/constant.h"
 #include "src/tint/sem/depth_texture.h"
 #include "src/tint/sem/external_texture.h"
 #include "src/tint/sem/f16.h"
@@ -128,7 +130,7 @@
         traits::EnableIfIsNotType<traits::Decay<traits::NthTypeOf<0, TYPES..., void>>, Source>;
 
     /// VarOptionals is a helper for accepting a number of optional, extra
-    /// arguments for Var() and Global().
+    /// arguments for Var() and GlobalVar().
     struct VarOptionals {
         template <typename... ARGS>
         explicit VarOptionals(ARGS&&... args) {
@@ -162,6 +164,9 @@
     /// SemNodeAllocator is an alias to BlockAllocator<sem::Node>
     using SemNodeAllocator = utils::BlockAllocator<sem::Node>;
 
+    /// ConstantAllocator is an alias to BlockAllocator<sem::Constant>
+    using ConstantAllocator = utils::BlockAllocator<sem::Constant>;
+
     /// Constructor
     ProgramBuilder();
 
@@ -228,6 +233,12 @@
         return sem_nodes_;
     }
 
+    /// @returns a reference to the program's semantic constant storage
+    ConstantAllocator& ConstantNodes() {
+        AssertNotMoved();
+        return constant_nodes_;
+    }
+
     /// @returns a reference to the program's AST root Module
     ast::Module& AST() {
         AssertNotMoved();
@@ -331,9 +342,8 @@
     }
 
     /// Creates a new sem::Node owned by the ProgramBuilder.
-    /// When the ProgramBuilder is destructed, the sem::Node will also be
-    /// destructed.
-    /// @param args the arguments to pass to the type constructor
+    /// When the ProgramBuilder is destructed, the sem::Node will also be destructed.
+    /// @param args the arguments to pass to the constructor
     /// @returns the node pointer
     template <typename T, typename... ARGS>
     traits::EnableIf<traits::IsTypeOrDerived<T, sem::Node> &&
@@ -344,6 +354,16 @@
         return sem_nodes_.Create<T>(std::forward<ARGS>(args)...);
     }
 
+    /// Creates a new sem::Constant owned by the ProgramBuilder.
+    /// When the ProgramBuilder is destructed, the sem::Node will also be destructed.
+    /// @param args the arguments to pass to the constructor
+    /// @returns the node pointer
+    template <typename T, typename... ARGS>
+    traits::EnableIf<traits::IsTypeOrDerived<T, sem::Constant>, T>* create(ARGS&&... args) {
+        AssertNotMoved();
+        return constant_nodes_.Create<T>(std::forward<ARGS>(args)...);
+    }
+
     /// Creates a new sem::Type owned by the ProgramBuilder.
     /// When the ProgramBuilder is destructed, owned ProgramBuilder and the
     /// returned`Type` will also be destructed.
@@ -1367,6 +1387,35 @@
     /// @param type the variable type
     /// @param constructor constructor expression
     /// @param attributes optional variable attributes
+    /// @returns an `ast::Const` with the given name and type
+    template <typename NAME>
+    const ast::Const* Const(NAME&& name,
+                            const ast::Type* type,
+                            const ast::Expression* constructor,
+                            ast::AttributeList attributes = {}) {
+        return create<ast::Const>(Sym(std::forward<NAME>(name)), type, constructor, attributes);
+    }
+
+    /// @param source the variable source
+    /// @param name the variable name
+    /// @param type the variable type
+    /// @param constructor constructor expression
+    /// @param attributes optional variable attributes
+    /// @returns an `ast::Const` with the given name and type
+    template <typename NAME>
+    const ast::Const* Const(const Source& source,
+                            NAME&& name,
+                            const ast::Type* type,
+                            const ast::Expression* constructor,
+                            ast::AttributeList attributes = {}) {
+        return create<ast::Const>(source, Sym(std::forward<NAME>(name)), type, constructor,
+                                  attributes);
+    }
+
+    /// @param name the variable name
+    /// @param type the variable type
+    /// @param constructor constructor expression
+    /// @param attributes optional variable attributes
     /// @returns an `ast::Let` with the given name and type
     template <typename NAME>
     const ast::Let* Let(NAME&& name,
@@ -1429,7 +1478,7 @@
     /// @returns a new `ast::Var`, which is automatically registered as a global variable with the
     /// ast::Module.
     template <typename NAME, typename... OPTIONAL, typename = DisableIfSource<NAME>>
-    const ast::Var* Global(NAME&& name, const ast::Type* type, OPTIONAL&&... optional) {
+    const ast::Var* GlobalVar(NAME&& name, const ast::Type* type, OPTIONAL&&... optional) {
         auto* var = Var(std::forward<NAME>(name), type, std::forward<OPTIONAL>(optional)...);
         AST().AddGlobalVariable(var);
         return var;
@@ -1449,10 +1498,10 @@
     /// @returns a new `ast::Var`, which is automatically registered as a global variable with the
     /// ast::Module.
     template <typename NAME, typename... OPTIONAL>
-    const ast::Var* Global(const Source& source,
-                           NAME&& name,
-                           const ast::Type* type,
-                           OPTIONAL&&... optional) {
+    const ast::Var* GlobalVar(const Source& source,
+                              NAME&& name,
+                              const ast::Type* type,
+                              OPTIONAL&&... optional) {
         auto* var =
             Var(source, std::forward<NAME>(name), type, std::forward<OPTIONAL>(optional)...);
         AST().AddGlobalVariable(var);
@@ -1463,14 +1512,14 @@
     /// @param type the variable type
     /// @param constructor constructor expression
     /// @param attributes optional variable attributes
-    /// @returns an `ast::Let` constructed by calling Let() with the arguments of `args`, which is
-    /// automatically registered as a global variable with the ast::Module.
+    /// @returns an `ast::Const` constructed by calling Const() with the arguments of `args`, which
+    /// is automatically registered as a global variable with the ast::Module.
     template <typename NAME>
-    const ast::Let* GlobalConst(NAME&& name,
-                                const ast::Type* type,
-                                const ast::Expression* constructor,
-                                ast::AttributeList attributes = {}) {
-        auto* var = Let(std::forward<NAME>(name), type, constructor, std::move(attributes));
+    const ast::Const* GlobalConst(NAME&& name,
+                                  const ast::Type* type,
+                                  const ast::Expression* constructor,
+                                  ast::AttributeList attributes = {}) {
+        auto* var = Const(std::forward<NAME>(name), type, constructor, std::move(attributes));
         AST().AddGlobalVariable(var);
         return var;
     }
@@ -1480,16 +1529,17 @@
     /// @param type the variable type
     /// @param constructor constructor expression
     /// @param attributes optional variable attributes
-    /// @returns a const `ast::Let` constructed by calling Var() with the
+    /// @returns a const `ast::Const` constructed by calling Var() with the
     /// arguments of `args`, which is automatically registered as a global
     /// variable with the ast::Module.
     template <typename NAME>
-    const ast::Let* GlobalConst(const Source& source,
-                                NAME&& name,
-                                const ast::Type* type,
-                                const ast::Expression* constructor,
-                                ast::AttributeList attributes = {}) {
-        auto* var = Let(source, std::forward<NAME>(name), type, constructor, std::move(attributes));
+    const ast::Const* GlobalConst(const Source& source,
+                                  NAME&& name,
+                                  const ast::Type* type,
+                                  const ast::Expression* constructor,
+                                  ast::AttributeList attributes = {}) {
+        auto* var =
+            Const(source, std::forward<NAME>(name), type, constructor, std::move(attributes));
         AST().AddGlobalVariable(var);
         return var;
     }
@@ -2716,6 +2766,7 @@
     sem::Manager types_;
     ASTNodeAllocator ast_nodes_;
     SemNodeAllocator sem_nodes_;
+    ConstantAllocator constant_nodes_;
     ast::Module* ast_;
     sem::Info sem_;
     SymbolTable symbols_{id_};
diff --git a/src/tint/program_test.cc b/src/tint/program_test.cc
index 3bdf11a..ae6aabe 100644
--- a/src/tint/program_test.cc
+++ b/src/tint/program_test.cc
@@ -46,7 +46,7 @@
 }
 
 TEST_F(ProgramTest, Assert_GlobalVariable) {
-    Global("var", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
 
     Program program(std::move(*this));
     EXPECT_TRUE(program.IsValid());
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 2a5169b..2bcae95 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -36,6 +36,7 @@
 #include "src/tint/sem/builtin_type.h"
 #include "src/tint/sem/depth_texture.h"
 #include "src/tint/sem/sampled_texture.h"
+#include "src/tint/transform/spirv_atomic.h"
 
 // Terms:
 //    CFG: the control flow graph of the function, where basic blocks are the
@@ -501,6 +502,38 @@
 }
 
 // @param opcode a SPIR-V opcode
+// @returns true if the given instruction is an atomic operation.
+bool IsAtomicOp(SpvOp opcode) {
+    switch (opcode) {
+        case SpvOpAtomicLoad:
+        case SpvOpAtomicStore:
+        case SpvOpAtomicExchange:
+        case SpvOpAtomicCompareExchange:
+        case SpvOpAtomicCompareExchangeWeak:
+        case SpvOpAtomicIIncrement:
+        case SpvOpAtomicIDecrement:
+        case SpvOpAtomicIAdd:
+        case SpvOpAtomicISub:
+        case SpvOpAtomicSMin:
+        case SpvOpAtomicUMin:
+        case SpvOpAtomicSMax:
+        case SpvOpAtomicUMax:
+        case SpvOpAtomicAnd:
+        case SpvOpAtomicOr:
+        case SpvOpAtomicXor:
+        case SpvOpAtomicFlagTestAndSet:
+        case SpvOpAtomicFlagClear:
+        case SpvOpAtomicFMinEXT:
+        case SpvOpAtomicFMaxEXT:
+        case SpvOpAtomicFAddEXT:
+            return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+// @param opcode a SPIR-V opcode
 // @returns true if the given instruction is an image sampling, gather,
 // or gather-compare operation.
 bool IsImageSamplingOrGatherOrDrefGather(SpvOp opcode) {
@@ -991,11 +1024,13 @@
         [&](const Struct* struct_type) -> bool {
             const auto& members = struct_type->members;
             index_prefix.push_back(0);
-            for (int i = 0; i < static_cast<int>(members.size()); ++i) {
-                index_prefix.back() = i;
+            for (size_t i = 0; i < members.size(); ++i) {
+                index_prefix.back() = static_cast<int>(i);
                 ast::AttributeList member_attrs(*attrs);
                 if (!parser_impl_.ConvertPipelineDecorations(
-                        struct_type, parser_impl_.GetMemberPipelineDecorations(*struct_type, i),
+                        struct_type,
+                        parser_impl_.GetMemberPipelineDecorations(*struct_type,
+                                                                  static_cast<int>(i)),
                         &member_attrs)) {
                     return false;
                 }
@@ -1045,7 +1080,7 @@
                         store_dest = builder_.MemberAccessor(
                             store_dest,
                             builder_.Expr(parser_impl_.GetMemberName(*struct_type, index)));
-                        current_type = struct_type->members[index];
+                        current_type = struct_type->members[static_cast<size_t>(index)];
                     });
             }
 
@@ -1141,8 +1176,9 @@
                         &member_attrs)) {
                     return false;
                 }
-                if (!EmitPipelineOutput(var_name, var_type, &member_attrs, index_prefix, members[i],
-                                        forced_member_type, return_members, return_exprs)) {
+                if (!EmitPipelineOutput(var_name, var_type, &member_attrs, index_prefix,
+                                        members[static_cast<size_t>(i)], forced_member_type,
+                                        return_members, return_exprs)) {
                     return false;
                 }
                 // Copy the location as updated by nested expansion of the member.
@@ -1190,7 +1226,7 @@
                         load_source = builder_.MemberAccessor(
                             load_source,
                             builder_.Expr(parser_impl_.GetMemberName(*struct_type, index)));
-                        current_type = struct_type->members[index];
+                        current_type = struct_type->members[static_cast<size_t>(index)];
                     });
             }
 
@@ -1792,7 +1828,7 @@
             // The current block is a header.
             const auto header = block_id;
             const auto* header_info = block_info;
-            const auto depth = 1 + top->depth;
+            const auto depth = static_cast<size_t>(1 + top->depth);
             const auto ct = header_info->continue_for_header;
             if (ct != 0) {
                 // The current block is a loop header.
@@ -3487,6 +3523,10 @@
         return EmitImageAccess(inst);
     }
 
+    if (IsAtomicOp(inst.opcode())) {
+        return EmitAtomicOp(inst);
+    }
+
     switch (inst.opcode()) {
         case SpvOpNop:
             return true;
@@ -4273,7 +4313,7 @@
     // This is structurally similar to creating an access chain, but
     // the SPIR-V instruction has literal indices instead of IDs for indices.
 
-    auto composite_index = 0;
+    auto composite_index = 0u;
     auto first_index_position = 1;
     TypedExpression current_expr(MakeOperand(inst, composite_index));
     if (!current_expr) {
@@ -4317,13 +4357,14 @@
     // hierarchy, maintaining |current_type_id| as the SPIR-V ID of the type of
     // the object pointed to after processing the previous indices.
     const auto num_in_operands = inst.NumInOperands();
-    for (uint32_t index = index_start; index < num_in_operands; ++index) {
+    for (uint32_t index = static_cast<uint32_t>(index_start); index < num_in_operands; ++index) {
         const uint32_t index_val = inst.GetSingleWordInOperand(index);
 
         const auto* current_type_inst = def_use_mgr_->GetDef(current_type_id);
         if (!current_type_inst) {
             Fail() << "composite type %" << current_type_id << " is invalid after following "
-                   << (index - index_start) << " indices: " << inst.PrettyPrint();
+                   << (index - static_cast<uint32_t>(index_start))
+                   << " indices: " << inst.PrettyPrint();
             return {};
         }
         const char* operation_name = nullptr;
@@ -4613,7 +4654,7 @@
     // but only if they are defined in this function as well.
     auto require_named_const_def = [&](const spvtools::opt::Instruction& inst,
                                        int in_operand_index) {
-        const auto id = inst.GetSingleWordInOperand(in_operand_index);
+        const auto id = inst.GetSingleWordInOperand(static_cast<uint32_t>(in_operand_index));
         auto* const operand_def = GetDefInfo(id);
         if (operand_def) {
             operand_def->requires_named_const_def = true;
@@ -4881,7 +4922,7 @@
 
 bool FunctionEmitter::EmitControlBarrier(const spvtools::opt::Instruction& inst) {
     uint32_t operands[3];
-    for (int i = 0; i < 3; i++) {
+    for (uint32_t i = 0; i < 3; i++) {
         auto id = inst.GetSingleWordInOperand(i);
         if (auto* constant = constant_mgr_->FindDeclaredConstant(id)) {
             operands[i] = constant->GetU32();
@@ -4899,7 +4940,7 @@
                       << "expected Workgroup (2), got: " << execution;
     }
     if (semantics & SpvMemorySemanticsAcquireReleaseMask) {
-        semantics &= ~SpvMemorySemanticsAcquireReleaseMask;
+        semantics &= ~static_cast<uint32_t>(SpvMemorySemanticsAcquireReleaseMask);
     } else {
         return Fail() << "control barrier semantics requires acquire and release";
     }
@@ -4908,14 +4949,14 @@
             return Fail() << "workgroupBarrier requires workgroup memory scope";
         }
         AddStatement(create<ast::CallStatement>(builder_.Call("workgroupBarrier")));
-        semantics &= ~SpvMemorySemanticsWorkgroupMemoryMask;
+        semantics &= ~static_cast<uint32_t>(SpvMemorySemanticsWorkgroupMemoryMask);
     }
     if (semantics & SpvMemorySemanticsUniformMemoryMask) {
         if (memory != SpvScopeDevice) {
             return Fail() << "storageBarrier requires device memory scope";
         }
         AddStatement(create<ast::CallStatement>(builder_.Call("storageBarrier")));
-        semantics &= ~SpvMemorySemanticsUniformMemoryMask;
+        semantics &= ~static_cast<uint32_t>(SpvMemorySemanticsUniformMemoryMask);
     }
     if (semantics) {
         return Fail() << "unsupported control barrier semantics: " << semantics;
@@ -5417,6 +5458,115 @@
     return Fail() << "unhandled image query: " << inst.PrettyPrint();
 }
 
+bool FunctionEmitter::EmitAtomicOp(const spvtools::opt::Instruction& inst) {
+    auto emit_atomic = [&](sem::BuiltinType builtin, std::initializer_list<TypedExpression> args) {
+        // Split args into params and expressions
+        ast::ParameterList params;
+        params.reserve(args.size());
+        ast::ExpressionList exprs;
+        exprs.reserve(args.size());
+        size_t i = 0;
+        for (auto& a : args) {
+            params.emplace_back(builder_.Param("p" + std::to_string(i++), a.type->Build(builder_)));
+            exprs.emplace_back(a.expr);
+        }
+
+        // Function return type
+        const ast::Type* ret_type = nullptr;
+        if (inst.type_id() != 0) {
+            ret_type = parser_impl_.ConvertType(inst.type_id())->Build(builder_);
+        } else {
+            ret_type = builder_.ty.void_();
+        }
+
+        // Emit stub, will be removed by transform::SpirvAtomic
+        auto sym = builder_.Symbols().New(std::string("stub_") + sem::str(builtin));
+        auto* stub_deco =
+            builder_.ASTNodes().Create<transform::SpirvAtomic::Stub>(builder_.ID(), builtin);
+        auto* stub =
+            create<ast::Function>(Source{}, sym, std::move(params), ret_type,
+                                  /* body */ nullptr,
+                                  ast::AttributeList{
+                                      stub_deco,
+                                      builder_.Disable(ast::DisabledValidation::kFunctionHasNoBody),
+                                  },
+                                  ast::AttributeList{});
+        builder_.AST().AddFunction(stub);
+
+        // Emit call to stub, will be replaced with call to atomic builtin by transform::SpirvAtomic
+        auto* call = builder_.Call(Source{}, sym, exprs);
+        if (inst.type_id() != 0) {
+            auto* result_type = parser_impl_.ConvertType(inst.type_id());
+            TypedExpression expr{result_type, call};
+            return EmitConstDefOrWriteToHoistedVar(inst, expr);
+        }
+        AddStatement(create<ast::CallStatement>(call));
+
+        return true;
+    };
+
+    auto oper = [&](uint32_t index) -> TypedExpression {  //
+        return MakeOperand(inst, index);
+    };
+
+    auto lit = [&](int v) -> TypedExpression {
+        auto* result_type = parser_impl_.ConvertType(inst.type_id());
+        if (result_type->Is<I32>()) {
+            return TypedExpression(result_type, builder_.Expr(i32(v)));
+        } else if (result_type->Is<U32>()) {
+            return TypedExpression(result_type, builder_.Expr(u32(v)));
+        }
+        return {};
+    };
+
+    switch (inst.opcode()) {
+        case SpvOpAtomicLoad:
+            return emit_atomic(sem::BuiltinType::kAtomicLoad, {oper(/*ptr*/ 0)});
+        case SpvOpAtomicStore:
+            return emit_atomic(sem::BuiltinType::kAtomicStore,
+                               {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicExchange:
+            return emit_atomic(sem::BuiltinType::kAtomicExchange,
+                               {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicCompareExchange:
+        case SpvOpAtomicCompareExchangeWeak:
+            return emit_atomic(sem::BuiltinType::kAtomicCompareExchangeWeak,
+                               {oper(/*ptr*/ 0), /*value*/ oper(5), /*comparator*/ oper(4)});
+        case SpvOpAtomicIIncrement:
+            return emit_atomic(sem::BuiltinType::kAtomicAdd, {oper(/*ptr*/ 0), lit(1)});
+        case SpvOpAtomicIDecrement:
+            return emit_atomic(sem::BuiltinType::kAtomicSub, {oper(/*ptr*/ 0), lit(1)});
+        case SpvOpAtomicIAdd:
+            return emit_atomic(sem::BuiltinType::kAtomicAdd, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicISub:
+            return emit_atomic(sem::BuiltinType::kAtomicSub, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicSMin:
+            return emit_atomic(sem::BuiltinType::kAtomicMin, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicUMin:
+            return emit_atomic(sem::BuiltinType::kAtomicMin, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicSMax:
+            return emit_atomic(sem::BuiltinType::kAtomicMax, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicUMax:
+            return emit_atomic(sem::BuiltinType::kAtomicMax, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicAnd:
+            return emit_atomic(sem::BuiltinType::kAtomicAnd, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicOr:
+            return emit_atomic(sem::BuiltinType::kAtomicOr, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicXor:
+            return emit_atomic(sem::BuiltinType::kAtomicXor, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicFlagTestAndSet:
+        case SpvOpAtomicFlagClear:
+        case SpvOpAtomicFMinEXT:
+        case SpvOpAtomicFMaxEXT:
+        case SpvOpAtomicFAddEXT:
+            return Fail() << "unsupported atomic op: " << inst.PrettyPrint();
+
+        default:
+            break;
+    }
+    return Fail() << "unhandled atomic op: " << inst.PrettyPrint();
+}
+
 ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
     const spvtools::opt::Instruction& inst) {
     if (!parser_impl_.success()) {
@@ -5454,7 +5604,7 @@
     }
     ast::TextureDimension dim = texture_type->dims;
     // Number of regular coordinates.
-    uint32_t num_axes = ast::NumCoordinateAxes(dim);
+    uint32_t num_axes = static_cast<uint32_t>(ast::NumCoordinateAxes(dim));
     bool is_arrayed = ast::IsTextureArray(dim);
     if ((num_axes == 0) || (num_axes > 3)) {
         Fail() << "unsupported image dimensionality for " << texture_type->TypeInfo().name
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index b1c3f57..ae9ef31 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -1060,7 +1060,7 @@
     /// Emits a texture builtin function call for a SPIR-V instruction that
     /// accesses an image or sampled image.
     /// @param inst the SPIR-V instruction
-    /// @returns an expression
+    /// @returns true on success, false on error
     bool EmitImageAccess(const spvtools::opt::Instruction& inst);
 
     /// Emits statements to implement a SPIR-V image query.
@@ -1068,6 +1068,11 @@
     /// @returns an expression
     bool EmitImageQuery(const spvtools::opt::Instruction& inst);
 
+    /// Emits statements to implement a SPIR-V atomic op.
+    /// @param inst the SPIR-V instruction
+    /// @returns true on success, false on error
+    bool EmitAtomicOp(const spvtools::opt::Instruction& inst);
+
     /// Converts the given texel to match the type required for the storage
     /// texture with the given type. In WGSL the texel value is always provided
     /// as a 4-element vector, but the component type is determined by the
diff --git a/src/tint/reader/spirv/parser.cc b/src/tint/reader/spirv/parser.cc
index f430d94..ac43b9e 100644
--- a/src/tint/reader/spirv/parser.cc
+++ b/src/tint/reader/spirv/parser.cc
@@ -22,6 +22,7 @@
 #include "src/tint/transform/manager.h"
 #include "src/tint/transform/remove_unreachable_statements.h"
 #include "src/tint/transform/simplify_pointers.h"
+#include "src/tint/transform/spirv_atomic.h"
 #include "src/tint/transform/unshadow.h"
 
 namespace tint::reader::spirv {
@@ -55,6 +56,7 @@
     manager.Add<transform::DecomposeStridedMatrix>();
     manager.Add<transform::DecomposeStridedArray>();
     manager.Add<transform::RemoveUnreachableStatements>();
+    manager.Add<transform::SpirvAtomic>();
     return manager.Run(&program).program;
 }
 
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 1ebf0b9..b34f66b 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -816,7 +816,7 @@
     /// Returns false and emits a diagnostic on error.
     auto set_param = [this, composite_def](uint32_t* id_ptr, uint32_t* value_ptr,
                                            int index) -> bool {
-        const auto id = composite_def->GetSingleWordInOperand(index);
+        const auto id = composite_def->GetSingleWordInOperand(static_cast<uint32_t>(index));
         const auto* def = def_use_mgr_->GetDef(id);
         if (!def || (def->opcode() != SpvOpSpecConstant && def->opcode() != SpvOpConstant) ||
             (def->NumInOperands() != 1)) {
@@ -1336,7 +1336,7 @@
                     },
                     [&](const U32*) {
                         return create<ast::IntLiteralExpression>(
-                            Source{}, static_cast<uint64_t>(literal_value),
+                            Source{}, static_cast<int64_t>(literal_value),
                             ast::IntLiteralExpression::Suffix::kU);
                     },
                     [&](const F32*) {
@@ -1715,8 +1715,8 @@
                                                         int member_index) {
     // Yes, I could have used std::copy_if or std::copy_if.
     DecorationList result;
-    for (const auto& deco :
-         GetDecorationsForMember(struct_id_for_symbol_[struct_type.name], member_index)) {
+    for (const auto& deco : GetDecorationsForMember(struct_id_for_symbol_[struct_type.name],
+                                                    static_cast<uint32_t>(member_index))) {
         if (IsPipelineDecoration(deco)) {
             result.emplace_back(deco);
         }
@@ -2721,7 +2721,7 @@
         Fail() << "no structure type registered for symbol";
         return "";
     }
-    return namer_.GetMemberName(where->second, member_index);
+    return namer_.GetMemberName(where->second, static_cast<uint32_t>(member_index));
 }
 
 WorkgroupSizeInfo::WorkgroupSizeInfo() = default;
diff --git a/src/tint/reader/spirv/parser_impl.h b/src/tint/reader/spirv/parser_impl.h
index 6addf6c..ce790d3 100644
--- a/src/tint/reader/spirv/parser_impl.h
+++ b/src/tint/reader/spirv/parser_impl.h
@@ -22,8 +22,18 @@
 #include <utility>
 #include <vector>
 
+#include "src/tint/utils/compiler_macros.h"
+
 #if TINT_BUILD_SPV_READER
+TINT_BEGIN_DISABLE_WARNING(NEWLINE_EOF);
+TINT_BEGIN_DISABLE_WARNING(OLD_STYLE_CAST);
+TINT_BEGIN_DISABLE_WARNING(SIGN_CONVERSION);
+TINT_BEGIN_DISABLE_WARNING(WEAK_VTABLES);
 #include "source/opt/ir_context.h"
+TINT_END_DISABLE_WARNING(WEAK_VTABLES);
+TINT_END_DISABLE_WARNING(SIGN_CONVERSION);
+TINT_END_DISABLE_WARNING(OLD_STYLE_CAST);
+TINT_END_DISABLE_WARNING(NEWLINE_EOF);
 #endif
 
 #include "src/tint/program_builder.h"
diff --git a/src/tint/reader/spirv/parser_impl_test_helper.h b/src/tint/reader/spirv/parser_impl_test_helper.h
index 7362a2d..f2c2dfe 100644
--- a/src/tint/reader/spirv/parser_impl_test_helper.h
+++ b/src/tint/reader/spirv/parser_impl_test_helper.h
@@ -21,8 +21,18 @@
 #include <utility>
 #include <vector>
 
+#include "src/tint/utils/compiler_macros.h"
+
 #if TINT_BUILD_SPV_READER
+TINT_BEGIN_DISABLE_WARNING(NEWLINE_EOF);
+TINT_BEGIN_DISABLE_WARNING(OLD_STYLE_CAST);
+TINT_BEGIN_DISABLE_WARNING(SIGN_CONVERSION);
+TINT_BEGIN_DISABLE_WARNING(WEAK_VTABLES);
 #include "source/opt/ir_context.h"
+TINT_END_DISABLE_WARNING(WEAK_VTABLES);
+TINT_END_DISABLE_WARNING(SIGN_CONVERSION);
+TINT_END_DISABLE_WARNING(OLD_STYLE_CAST);
+TINT_END_DISABLE_WARNING(NEWLINE_EOF);
 #endif
 
 #include "gtest/gtest.h"
diff --git a/src/tint/reader/spirv/parser_type.cc b/src/tint/reader/spirv/parser_type.cc
index 3332cd4..db8c01b 100644
--- a/src/tint/reader/spirv/parser_type.cc
+++ b/src/tint/reader/spirv/parser_type.cc
@@ -163,6 +163,12 @@
     return b.ty.i32();
 }
 
+Type::Type() = default;
+Type::Type(const Type&) = default;
+Type::~Type() = default;
+
+Texture::~Texture() = default;
+
 Pointer::Pointer(const Type* t, ast::StorageClass s) : type(t), storage_class(s) {}
 Pointer::Pointer(const Pointer&) = default;
 
diff --git a/src/tint/reader/spirv/parser_type.h b/src/tint/reader/spirv/parser_type.h
index 605ac9b..9543b51 100644
--- a/src/tint/reader/spirv/parser_type.h
+++ b/src/tint/reader/spirv/parser_type.h
@@ -40,6 +40,13 @@
 /// Type is the base class for all types
 class Type : public Castable<Type> {
   public:
+    /// Constructor
+    Type();
+    /// Copy constructor
+    Type(const Type&);
+    /// Destructor
+    ~Type() override;
+
     /// @param b the ProgramBuilder used to construct the AST types
     /// @returns the constructed ast::Type node for the given type
     virtual const ast::Type* Build(ProgramBuilder& b) const = 0;
@@ -314,6 +321,8 @@
 
 /// Base class for texture types
 struct Texture : public Castable<Texture, Type> {
+    ~Texture() override;
+
     /// Constructor
     /// @param d the texture dimensions
     explicit Texture(ast::TextureDimension d);
diff --git a/src/tint/reader/spirv/usage_test.cc b/src/tint/reader/spirv/usage_test.cc
index d01d1a2..bb64bb3 100644
--- a/src/tint/reader/spirv/usage_test.cc
+++ b/src/tint/reader/spirv/usage_test.cc
@@ -45,7 +45,7 @@
 }
 
 TEST_F(SpvParserTest, Usage_Equality_OneDifference) {
-    const int num_usages = 9;
+    const size_t num_usages = 9u;
     std::vector<Usage> usages(num_usages);
     usages[1].AddSampler();
     usages[2].AddComparisonSampler();
@@ -55,8 +55,8 @@
     usages[6].AddDepthTexture();
     usages[7].AddStorageReadTexture();
     usages[8].AddStorageWriteTexture();
-    for (int i = 0; i < num_usages; ++i) {
-        for (int j = 0; j < num_usages; ++j) {
+    for (size_t i = 0; i < num_usages; ++i) {
+        for (size_t j = 0; j < num_usages; ++j) {
             const auto& lhs = usages[i];
             const auto& rhs = usages[j];
             if (i == j) {
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 716075e..ade723c 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -412,7 +412,7 @@
     // clang-format on
 
     // -?
-    int64_t sign_bit = 0;
+    uint64_t sign_bit = 0;
     if (matches(end, "-")) {
         sign_bit = 1;
         end++;
@@ -794,7 +794,7 @@
     const bool overflow = errno == ERANGE;
 
     if (end_ptr) {
-        advance(end_ptr - start_ptr);
+        advance(static_cast<size_t>(end_ptr - start_ptr));
     }
 
     if (matches(pos(), "u")) {
@@ -1088,6 +1088,9 @@
     if (str == "case") {
         return {Token::Type::kCase, source, "case"};
     }
+    if (str == "const") {
+        return {Token::Type::kConst, source, "const"};
+    }
     if (str == "continue") {
         return {Token::Type::kContinue, source, "continue"};
     }
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index ab4ec7e..bf32a52 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -144,18 +144,18 @@
     // Test that line comments are ended by blankspace characters other than
     // space, horizontal tab, left-to-right mark, and right-to-left mark.
     auto* c = GetParam();
-    std::string src = "let// This is a comment";
+    std::string src = "const// This is a comment";
     src += c;
     src += "ident";
     Source::File file("", src);
     Lexer l(&file);
 
     auto t = l.next();
-    EXPECT_TRUE(t.Is(Token::Type::kLet));
+    EXPECT_TRUE(t.Is(Token::Type::kConst));
     EXPECT_EQ(t.source().range.begin.line, 1u);
     EXPECT_EQ(t.source().range.begin.column, 1u);
     EXPECT_EQ(t.source().range.end.line, 1u);
-    EXPECT_EQ(t.source().range.end.column, 4u);
+    EXPECT_EQ(t.source().range.end.column, 6u);
 
     auto is_same_line = [](std::string_view v) {
         return v == kSpace || v == kHTab || v == kL2R || v == kR2L;
@@ -930,6 +930,7 @@
                     TokenData{"bool", Token::Type::kBool},
                     TokenData{"break", Token::Type::kBreak},
                     TokenData{"case", Token::Type::kCase},
+                    TokenData{"const", Token::Type::kConst},
                     TokenData{"continue", Token::Type::kContinue},
                     TokenData{"continuing", Token::Type::kContinuing},
                     TokenData{"default", Token::Type::kDefault},
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 854b276..deaa17c 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -124,10 +124,10 @@
 
 // https://gpuweb.github.io/gpuweb/wgsl.html#reserved-keywords
 bool is_reserved(Token t) {
-    return t == "asm" || t == "bf16" || t == "const" || t == "do" || t == "enum" || t == "f64" ||
-           t == "handle" || t == "i8" || t == "i16" || t == "i64" || t == "mat" ||
-           t == "premerge" || t == "regardless" || t == "typedef" || t == "u8" || t == "u16" ||
-           t == "u64" || t == "unless" || t == "using" || t == "vec" || t == "void" || t == "while";
+    return t == "asm" || t == "bf16" || t == "do" || t == "enum" || t == "f64" || t == "handle" ||
+           t == "i8" || t == "i16" || t == "i64" || t == "mat" || t == "premerge" ||
+           t == "regardless" || t == "typedef" || t == "u8" || t == "u16" || t == "u64" ||
+           t == "unless" || t == "using" || t == "vec" || t == "void" || t == "while";
 }
 
 /// Enter-exit counters for block token types.
@@ -437,8 +437,12 @@
         }
 
         if (gc.matched) {
-            if (!expect("'let' declaration", Token::Type::kSemicolon)) {
-                return Failure::kErrored;
+            // Avoid the cost of the string allocation for the common no-error case
+            if (!peek().Is(Token::Type::kSemicolon)) {
+                std::string kind = gc->Kind();
+                if (!expect("'" + kind + "' declaration", Token::Type::kSemicolon)) {
+                    return Failure::kErrored;
+                }
             }
 
             builder_.AST().AddGlobalVariable(gc.value);
@@ -537,13 +541,16 @@
         return Failure::kNoMatch;
     }
 
-    const ast::Expression* constructor = nullptr;
+    const ast::Expression* initalizer = nullptr;
     if (match(Token::Type::kEqual)) {
-        auto expr = expect_const_expr();
+        auto expr = logical_or_expression();
         if (expr.errored) {
             return Failure::kErrored;
         }
-        constructor = expr.value;
+        if (!expr.matched) {
+            return add_error(peek(), "missing initalizer for 'var' declaration");
+        }
+        initalizer = expr.value;
     }
 
     return create<ast::Var>(decl->source,                             // source
@@ -551,7 +558,7 @@
                             decl->type,                               // type
                             decl->storage_class,                      // storage class
                             decl->access,                             // access control
-                            constructor,                              // constructor
+                            initalizer,                               // constructor
                             std::move(attrs));                        // attributes
 }
 
@@ -561,10 +568,15 @@
 // global_const_initializer
 //  : EQUAL const_expr
 Maybe<const ast::Variable*> ParserImpl::global_constant_decl(ast::AttributeList& attrs) {
+    bool is_const = false;
     bool is_overridable = false;
     const char* use = nullptr;
-    if (match(Token::Type::kLet)) {
+    Source source;
+    if (match(Token::Type::kConst)) {
+        use = "'const' declaration";
+    } else if (match(Token::Type::kLet, &source)) {
         use = "'let' declaration";
+        deprecated(source, "module-scope 'let' has been replaced with 'const'");
     } else if (match(Token::Type::kOverride)) {
         use = "'override' declaration";
         is_overridable = true;
@@ -589,13 +601,23 @@
 
     const ast::Expression* initializer = nullptr;
     if (has_initializer) {
-        auto init = expect_const_expr();
-        if (init.errored) {
+        auto expr = logical_or_expression();
+        if (expr.errored) {
             return Failure::kErrored;
         }
-        initializer = std::move(init.value);
+        if (!expr.matched) {
+            return add_error(peek(), "missing initializer for " + std::string(use));
+        }
+        initializer = std::move(expr.value);
     }
 
+    if (is_const) {
+        return create<ast::Const>(decl->source,                             // source
+                                  builder_.Symbols().Register(decl->name),  // symbol
+                                  decl->type,                               // type
+                                  initializer,                              // constructor
+                                  std::move(attrs));                        // attributes
+    }
     if (is_overridable) {
         return create<ast::Override>(decl->source,                             // source
                                      builder_.Symbols().Register(decl->name),  // symbol
@@ -603,16 +625,16 @@
                                      initializer,                              // constructor
                                      std::move(attrs));                        // attributes
     }
-    return create<ast::Let>(decl->source,                             // source
-                            builder_.Symbols().Register(decl->name),  // symbol
-                            decl->type,                               // type
-                            initializer,                              // constructor
-                            std::move(attrs));                        // attributes
+    return create<ast::Const>(decl->source,                             // source
+                              builder_.Symbols().Register(decl->name),  // symbol
+                              decl->type,                               // type
+                              initializer,                              // constructor
+                              std::move(attrs));                        // attributes
 }
 
 // variable_decl
 //   : VAR variable_qualifier? variable_ident_decl
-Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl(bool allow_inferred) {
+Maybe<ParserImpl::VarDeclInfo> ParserImpl::variable_decl() {
     Source source;
     if (!match(Token::Type::kVar, &source)) {
         return Failure::kNoMatch;
@@ -627,7 +649,8 @@
         vq = explicit_vq.value;
     }
 
-    auto decl = expect_variable_ident_decl("variable declaration", allow_inferred);
+    auto decl = expect_variable_ident_decl("variable declaration",
+                                           /*allow_inferred = */ true);
     if (decl.errored) {
         return Failure::kErrored;
     }
@@ -1350,7 +1373,8 @@
         return Failure::kErrored;
     }
 
-    auto decl = expect_variable_ident_decl("struct member");
+    auto decl = expect_variable_ident_decl("struct member",
+                                           /*allow_inferred = */ false);
     if (decl.errored) {
         return Failure::kErrored;
     }
@@ -1486,7 +1510,8 @@
 Expect<ast::Parameter*> ParserImpl::expect_param() {
     auto attrs = attribute_list();
 
-    auto decl = expect_variable_ident_decl("parameter");
+    auto decl = expect_variable_ident_decl("parameter",
+                                           /*allow_inferred = */ false);
     if (decl.errored) {
         return Failure::kErrored;
     }
@@ -1769,6 +1794,34 @@
 //   | variable_decl EQUAL logical_or_expression
 //   | CONST variable_ident_decl EQUAL logical_or_expression
 Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
+    if (match(Token::Type::kConst)) {
+        auto decl = expect_variable_ident_decl("'const' declaration",
+                                               /*allow_inferred = */ true);
+        if (decl.errored) {
+            return Failure::kErrored;
+        }
+
+        if (!expect("'const' declaration", Token::Type::kEqual)) {
+            return Failure::kErrored;
+        }
+
+        auto constructor = logical_or_expression();
+        if (constructor.errored) {
+            return Failure::kErrored;
+        }
+        if (!constructor.matched) {
+            return add_error(peek(), "missing constructor for 'const' declaration");
+        }
+
+        auto* const_ = create<ast::Const>(decl->source,                             // source
+                                          builder_.Symbols().Register(decl->name),  // symbol
+                                          decl->type,                               // type
+                                          constructor.value,                        // constructor
+                                          ast::AttributeList{});                    // attributes
+
+        return create<ast::VariableDeclStatement>(decl->source, const_);
+    }
+
     if (match(Token::Type::kLet)) {
         auto decl = expect_variable_ident_decl("'let' declaration",
                                                /*allow_inferred = */ true);
@@ -1797,7 +1850,7 @@
         return create<ast::VariableDeclStatement>(decl->source, let);
     }
 
-    auto decl = variable_decl(/*allow_inferred = */ true);
+    auto decl = variable_decl();
     if (decl.errored) {
         return Failure::kErrored;
     }
@@ -3065,58 +3118,6 @@
     return Failure::kNoMatch;
 }
 
-// const_expr
-//   : type_decl PAREN_LEFT ((const_expr COMMA)? const_expr COMMA?)? PAREN_RIGHT
-//   | const_literal
-Expect<const ast::Expression*> ParserImpl::expect_const_expr() {
-    auto t = peek();
-    auto source = t.source();
-    if (t.IsLiteral()) {
-        auto lit = const_literal();
-        if (lit.errored) {
-            return Failure::kErrored;
-        }
-        if (!lit.matched) {
-            return add_error(peek(), "unable to parse constant literal");
-        }
-        return lit.value;
-    }
-
-    if (peek_is(Token::Type::kParenLeft, 1) || peek_is(Token::Type::kLessThan, 1)) {
-        auto type = expect_type("const_expr");
-        if (type.errored) {
-            return Failure::kErrored;
-        }
-
-        auto params = expect_paren_block("type constructor", [&]() -> Expect<ast::ExpressionList> {
-            ast::ExpressionList list;
-            while (continue_parsing()) {
-                if (peek_is(Token::Type::kParenRight)) {
-                    break;
-                }
-
-                auto arg = expect_const_expr();
-                if (arg.errored) {
-                    return Failure::kErrored;
-                }
-                list.emplace_back(arg.value);
-
-                if (!match(Token::Type::kComma)) {
-                    break;
-                }
-            }
-            return list;
-        });
-
-        if (params.errored) {
-            return Failure::kErrored;
-        }
-
-        return builder_.Construct(source, type.value, params.value);
-    }
-    return add_error(peek(), "unable to parse const_expr");
-}
-
 Maybe<ast::AttributeList> ParserImpl::attribute_list() {
     bool errored = false;
     ast::AttributeList attrs;
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index c6e696b..5857454 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -388,18 +388,15 @@
     /// @param attrs the list of attributes for the constant declaration.
     Maybe<const ast::Variable*> global_constant_decl(ast::AttributeList& attrs);
     /// Parses a `variable_decl` grammar element
-    /// @param allow_inferred if true, do not fail if variable decl does not
-    /// specify type
     /// @returns the parsed variable declaration info
-    Maybe<VarDeclInfo> variable_decl(bool allow_inferred = false);
+    Maybe<VarDeclInfo> variable_decl();
     /// Parses a `variable_ident_decl` grammar element, erroring on parse
     /// failure.
     /// @param use a description of what was being parsed if an error was raised.
     /// @param allow_inferred if true, do not fail if variable decl does not
     /// specify type
     /// @returns the identifier and type parsed or empty otherwise
-    Expect<TypedIdentifier> expect_variable_ident_decl(std::string_view use,
-                                                       bool allow_inferred = false);
+    Expect<TypedIdentifier> expect_variable_ident_decl(std::string_view use, bool allow_inferred);
     /// Parses a `variable_qualifier` grammar element
     /// @returns the variable qualifier information
     Maybe<VariableQualifier> variable_qualifier();
@@ -536,9 +533,6 @@
     /// Parses a `const_literal` grammar element
     /// @returns the const literal parsed or nullptr if none found
     Maybe<const ast::LiteralExpression*> const_literal();
-    /// Parses a `const_expr` grammar element, erroring on parse failure.
-    /// @returns the parsed constructor expression or nullptr on error
-    Expect<const ast::Expression*> expect_const_expr();
     /// Parses a `primary_expression` grammar element
     /// @returns the parsed expression or nullptr
     Maybe<const ast::Expression*> primary_expression();
diff --git a/src/tint/reader/wgsl/parser_impl_const_expr_test.cc b/src/tint/reader/wgsl/parser_impl_const_expr_test.cc
deleted file mode 100644
index 80536bd..0000000
--- a/src/tint/reader/wgsl/parser_impl_const_expr_test.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2020 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/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint::reader::wgsl {
-namespace {
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl) {
-    auto p = parser("vec2<f32>(1., 2.)");
-    auto e = p->expect_const_expr();
-    ASSERT_FALSE(p->has_error()) << p->error();
-    ASSERT_FALSE(e.errored);
-    ASSERT_TRUE(e->Is<ast::CallExpression>());
-
-    auto* t = e->As<ast::CallExpression>();
-    ASSERT_TRUE(t->target.type->Is<ast::Vector>());
-    EXPECT_EQ(t->target.type->As<ast::Vector>()->width, 2u);
-
-    ASSERT_EQ(t->args.size(), 2u);
-
-    ASSERT_TRUE(t->args[0]->Is<ast::FloatLiteralExpression>());
-    EXPECT_DOUBLE_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->value, 1.);
-    EXPECT_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->suffix,
-              ast::FloatLiteralExpression::Suffix::kNone);
-
-    ASSERT_TRUE(t->args[1]->Is<ast::FloatLiteralExpression>());
-    EXPECT_DOUBLE_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->value, 2.);
-    EXPECT_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->suffix,
-              ast::FloatLiteralExpression::Suffix::kNone);
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_Empty) {
-    auto p = parser("vec2<f32>()");
-    auto e = p->expect_const_expr();
-    ASSERT_FALSE(p->has_error()) << p->error();
-    ASSERT_FALSE(e.errored);
-    ASSERT_TRUE(e->Is<ast::CallExpression>());
-
-    auto* t = e->As<ast::CallExpression>();
-    ASSERT_TRUE(t->target.type->Is<ast::Vector>());
-    EXPECT_EQ(t->target.type->As<ast::Vector>()->width, 2u);
-
-    ASSERT_EQ(t->args.size(), 0u);
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_TrailingComma) {
-    auto p = parser("vec2<f32>(1., 2.,)");
-    auto e = p->expect_const_expr();
-    ASSERT_FALSE(p->has_error()) << p->error();
-    ASSERT_FALSE(e.errored);
-    ASSERT_TRUE(e->Is<ast::CallExpression>());
-
-    auto* t = e->As<ast::CallExpression>();
-    ASSERT_TRUE(t->target.type->Is<ast::Vector>());
-    EXPECT_EQ(t->target.type->As<ast::Vector>()->width, 2u);
-
-    ASSERT_EQ(t->args.size(), 2u);
-    ASSERT_TRUE(t->args[0]->Is<ast::LiteralExpression>());
-    ASSERT_TRUE(t->args[1]->Is<ast::LiteralExpression>());
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingRightParen) {
-    auto p = parser("vec2<f32>(1., 2.");
-    auto e = p->expect_const_expr();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:17: expected ')' for type constructor");
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingLeftParen) {
-    auto p = parser("vec2<f32> 1., 2.)");
-    auto e = p->expect_const_expr();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:11: expected '(' for type constructor");
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingComma) {
-    auto p = parser("vec2<f32>(1. 2.");
-    auto e = p->expect_const_expr();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:14: expected ')' for type constructor");
-}
-
-TEST_F(ParserImplTest, ConstExpr_InvalidExpr) {
-    auto p = parser("vec2<f32>(1., if(a) {})");
-    auto e = p->expect_const_expr();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:15: invalid type for const_expr");
-}
-
-TEST_F(ParserImplTest, ConstExpr_ConstLiteral) {
-    auto p = parser("true");
-    auto e = p->expect_const_expr();
-    ASSERT_FALSE(p->has_error()) << p->error();
-    ASSERT_FALSE(e.errored);
-    ASSERT_NE(e.value, nullptr);
-    ASSERT_TRUE(e.value->Is<ast::BoolLiteralExpression>());
-    EXPECT_TRUE(e.value->As<ast::BoolLiteralExpression>()->value);
-}
-
-TEST_F(ParserImplTest, ConstExpr_ConstLiteral_Invalid) {
-    auto p = parser("invalid");
-    auto e = p->expect_const_expr();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:1: unable to parse const_expr");
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeConstructor) {
-    auto p = parser("S(0)");
-
-    auto e = p->expect_const_expr();
-    ASSERT_FALSE(e.errored);
-    ASSERT_TRUE(e->Is<ast::CallExpression>());
-    ASSERT_NE(e->As<ast::CallExpression>()->target.type, nullptr);
-    ASSERT_TRUE(e->As<ast::CallExpression>()->target.type->Is<ast::TypeName>());
-    EXPECT_EQ(e->As<ast::CallExpression>()->target.type->As<ast::TypeName>()->name,
-              p->builder().Symbols().Get("S"));
-}
-
-TEST_F(ParserImplTest, ConstExpr_Recursion) {
-    std::stringstream out;
-    for (size_t i = 0; i < 200; i++) {
-        out << "f32(";
-    }
-    out << "1.0";
-    for (size_t i = 0; i < 200; i++) {
-        out << ")";
-    }
-    auto p = parser(out.str());
-    auto e = p->expect_const_expr();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:517: maximum parser recursive depth reached");
-}
-
-TEST_F(ParserImplTest, UnaryOp_Recursion) {
-    std::stringstream out;
-    for (size_t i = 0; i < 200; i++) {
-        out << "!";
-    }
-    out << "1.0";
-    auto p = parser(out.str());
-    auto e = p->unary_expression();
-    ASSERT_TRUE(p->has_error());
-    ASSERT_TRUE(e.errored);
-    ASSERT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:130: maximum parser recursive depth reached");
-}
-
-}  // namespace
-}  // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index fc141c0..48969a5 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -456,75 +456,54 @@
 }
 
 TEST_F(ParserImplErrorTest, FunctionMissingOpenLine) {
-    EXPECT(R"(let bar : vec2<f32> = vec2<f32>(1., 2.);
+    EXPECT(
+        R"(const bar : vec2<f32> = vec2<f32>(1., 2.);
   var a : f32 = bar[0];
   return;
 })",
-           R"(test.wgsl:2:17 error: unable to parse const_expr
-  var a : f32 = bar[0];
-                ^^^
-
-test.wgsl:3:3 error: statement found outside of function body
+        R"(test.wgsl:3:3 error: statement found outside of function body
   return;
   ^^^^^^
 )");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) {
-    EXPECT("let ^ : i32 = 1;",
-           R"(test.wgsl:1:5 error: expected identifier for 'let' declaration
-let ^ : i32 = 1;
-    ^
+    EXPECT("const ^ : i32 = 1;",
+           R"(test.wgsl:1:7 error: expected identifier for 'const' declaration
+const ^ : i32 = 1;
+      ^
 )");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstMissingSemicolon) {
-    EXPECT("let i : i32 = 1",
-           R"(test.wgsl:1:16 error: expected ';' for 'let' declaration
-let i : i32 = 1
-               ^
+    EXPECT("const i : i32 = 1",
+           R"(test.wgsl:1:18 error: expected ';' for 'const' declaration
+const i : i32 = 1
+                 ^
 )");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstMissingLParen) {
-    EXPECT("let i : vec2<i32> = vec2<i32>;",
-           R"(test.wgsl:1:30 error: expected '(' for type constructor
-let i : vec2<i32> = vec2<i32>;
-                             ^
+    EXPECT("const i : vec2<i32> = vec2<i32>;",
+           R"(test.wgsl:1:32 error: expected '(' for type constructor
+const i : vec2<i32> = vec2<i32>;
+                               ^
 )");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstMissingRParen) {
-    EXPECT("let i : vec2<i32> = vec2<i32>(1., 2.;",
-           R"(test.wgsl:1:37 error: expected ')' for type constructor
-let i : vec2<i32> = vec2<i32>(1., 2.;
-                                    ^
+    EXPECT("const i : vec2<i32> = vec2<i32>(1., 2.;",
+           R"(test.wgsl:1:39 error: expected ')' for type constructor
+const i : vec2<i32> = vec2<i32>(1., 2.;
+                                      ^
 )");
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteral) {
-    EXPECT("let i : vec2<i32> = vec2<i32>(!);",
-           R"(test.wgsl:1:31 error: unable to parse const_expr
-let i : vec2<i32> = vec2<i32>(!);
-                              ^
-)");
-}
-
-TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteralSpaceLessThan) {
-    EXPECT("let i = 1 < 2;",
-           R"(test.wgsl:1:11 error: expected ';' for 'let' declaration
-let i = 1 < 2;
-          ^
-)");
-}
-
-TEST_F(ParserImplErrorTest, GlobalDeclConstNotConstExpr) {
-    EXPECT(
-        "let a = 1;\n"
-        "let b = a;",
-        R"(test.wgsl:2:9 error: unable to parse const_expr
-let b = a;
-        ^
+    EXPECT("const i : vec2<i32> = vec2<i32>(!);",
+           R"(test.wgsl:1:34 error: unable to parse right side of ! expression
+const i : vec2<i32> = vec2<i32>(!);
+                                 ^
 )");
 }
 
@@ -533,8 +512,8 @@
 
     std::stringstream src;
     std::stringstream mkr;
-    src << "let i : i32 = ";
-    mkr << "              ";
+    src << "const i : i32 = ";
+    mkr << "                ";
     for (size_t i = 0; i < kMaxDepth + 8; i++) {
         src << "f32(";
         if (i < kMaxDepth) {
@@ -549,23 +528,113 @@
     }
     src << ";";
     std::stringstream err;
-    err << "test.wgsl:1:527 error: maximum parser recursive depth reached\n"
+    err << "test.wgsl:1:529 error: maximum parser recursive depth reached\n"
         << src.str() << "\n"
         << mkr.str() << "\n";
     EXPECT(src.str().c_str(), err.str().c_str());
 }
 
 TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingLParen) {
-    EXPECT("let i : vec2<i32> = vec2<i32> 1, 2);",
-           R"(test.wgsl:1:31 error: expected '(' for type constructor
+    EXPECT("const i : vec2<i32> = vec2<i32> 1, 2);",
+           R"(test.wgsl:1:33 error: expected '(' for type constructor
+const i : vec2<i32> = vec2<i32> 1, 2);
+                                ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingRParen) {
+    EXPECT("const i : vec2<i32> = vec2<i32>(1, 2;",
+           R"(test.wgsl:1:37 error: expected ')' for type constructor
+const i : vec2<i32> = vec2<i32>(1, 2;
+                                    ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetInvalidIdentifier) {
+    EXPECT(
+        "let ^ : i32 = 1;",
+        R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let ^ : i32 = 1;
+^^^
+
+test.wgsl:1:5 error: expected identifier for 'let' declaration
+let ^ : i32 = 1;
+    ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetMissingSemicolon) {
+    EXPECT(
+        "let i : i32 = 1",
+        R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let i : i32 = 1
+^^^
+
+test.wgsl:1:16 error: expected ';' for 'const' declaration
+let i : i32 = 1
+               ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetMissingLParen) {
+    EXPECT(
+        "let i : vec2<i32> = vec2<i32>;",
+        R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let i : vec2<i32> = vec2<i32>;
+^^^
+
+test.wgsl:1:30 error: expected '(' for type constructor
+let i : vec2<i32> = vec2<i32>;
+                             ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetMissingRParen) {
+    EXPECT(
+        "let i : vec2<i32> = vec2<i32>(1., 2.;",
+        R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let i : vec2<i32> = vec2<i32>(1., 2.;
+^^^
+
+test.wgsl:1:37 error: expected ')' for type constructor
+let i : vec2<i32> = vec2<i32>(1., 2.;
+                                    ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetBadConstLiteral) {
+    EXPECT("let i : vec2<i32> = vec2<i32>(!);",
+           R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let i : vec2<i32> = vec2<i32>(!);
+^^^
+
+test.wgsl:1:32 error: unable to parse right side of ! expression
+let i : vec2<i32> = vec2<i32>(!);
+                               ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetExprMissingLParen) {
+    EXPECT(
+        "let i : vec2<i32> = vec2<i32> 1, 2);",
+        R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let i : vec2<i32> = vec2<i32> 1, 2);
+^^^
+
+test.wgsl:1:31 error: expected '(' for type constructor
 let i : vec2<i32> = vec2<i32> 1, 2);
                               ^
 )");
 }
 
-TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingRParen) {
-    EXPECT("let i : vec2<i32> = vec2<i32>(1, 2;",
-           R"(test.wgsl:1:35 error: expected ')' for type constructor
+TEST_F(ParserImplErrorTest, GlobalDeclLetExprMissingRParen) {
+    EXPECT(
+        "let i : vec2<i32> = vec2<i32>(1, 2;",
+        R"(test.wgsl:1:1 warning: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+let i : vec2<i32> = vec2<i32>(1, 2;
+^^^
+
+test.wgsl:1:35 error: expected ')' for type constructor
 let i : vec2<i32> = vec2<i32>(1, 2;
                                   ^
 )");
diff --git a/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
index f5428fc..bfa678e 100644
--- a/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
@@ -18,7 +18,7 @@
 namespace tint::reader::wgsl {
 namespace {
 
-TEST_F(ParserImplTest, GlobalConstantDecl) {
+TEST_F(ParserImplTest, GlobalLetDecl) {
     auto p = parser("let a : f32 = 1.");
     auto attrs = p->attribute_list();
     EXPECT_FALSE(attrs.errored);
@@ -27,23 +27,23 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     EXPECT_TRUE(e.matched);
     EXPECT_FALSE(e.errored);
-    auto* let = e.value->As<ast::Let>();
-    ASSERT_NE(let, nullptr);
+    auto* const_ = e.value->As<ast::Const>();
+    ASSERT_NE(const_, nullptr);
 
-    EXPECT_EQ(let->symbol, p->builder().Symbols().Get("a"));
-    ASSERT_NE(let->type, nullptr);
-    EXPECT_TRUE(let->type->Is<ast::F32>());
+    EXPECT_EQ(const_->symbol, p->builder().Symbols().Get("a"));
+    ASSERT_NE(const_->type, nullptr);
+    EXPECT_TRUE(const_->type->Is<ast::F32>());
 
-    EXPECT_EQ(let->source.range.begin.line, 1u);
-    EXPECT_EQ(let->source.range.begin.column, 5u);
-    EXPECT_EQ(let->source.range.end.line, 1u);
-    EXPECT_EQ(let->source.range.end.column, 6u);
+    EXPECT_EQ(const_->source.range.begin.line, 1u);
+    EXPECT_EQ(const_->source.range.begin.column, 5u);
+    EXPECT_EQ(const_->source.range.end.line, 1u);
+    EXPECT_EQ(const_->source.range.end.column, 6u);
 
-    ASSERT_NE(let->constructor, nullptr);
-    EXPECT_TRUE(let->constructor->Is<ast::LiteralExpression>());
+    ASSERT_NE(const_->constructor, nullptr);
+    EXPECT_TRUE(const_->constructor->Is<ast::LiteralExpression>());
 }
 
-TEST_F(ParserImplTest, GlobalConstantDecl_Inferred) {
+TEST_F(ParserImplTest, GlobalLetDecl_Inferred) {
     auto p = parser("let a = 1.");
     auto attrs = p->attribute_list();
     EXPECT_FALSE(attrs.errored);
@@ -52,22 +52,22 @@
     EXPECT_FALSE(p->has_error()) << p->error();
     EXPECT_TRUE(e.matched);
     EXPECT_FALSE(e.errored);
-    auto* let = e.value->As<ast::Let>();
-    ASSERT_NE(let, nullptr);
+    auto* const_ = e.value->As<ast::Const>();
+    ASSERT_NE(const_, nullptr);
 
-    EXPECT_EQ(let->symbol, p->builder().Symbols().Get("a"));
-    EXPECT_EQ(let->type, nullptr);
+    EXPECT_EQ(const_->symbol, p->builder().Symbols().Get("a"));
+    EXPECT_EQ(const_->type, nullptr);
 
-    EXPECT_EQ(let->source.range.begin.line, 1u);
-    EXPECT_EQ(let->source.range.begin.column, 5u);
-    EXPECT_EQ(let->source.range.end.line, 1u);
-    EXPECT_EQ(let->source.range.end.column, 6u);
+    EXPECT_EQ(const_->source.range.begin.line, 1u);
+    EXPECT_EQ(const_->source.range.begin.column, 5u);
+    EXPECT_EQ(const_->source.range.end.line, 1u);
+    EXPECT_EQ(const_->source.range.end.column, 6u);
 
-    ASSERT_NE(let->constructor, nullptr);
-    EXPECT_TRUE(let->constructor->Is<ast::LiteralExpression>());
+    ASSERT_NE(const_->constructor, nullptr);
+    EXPECT_TRUE(const_->constructor->Is<ast::LiteralExpression>());
 }
 
-TEST_F(ParserImplTest, GlobalConstantDecl_InvalidExpression) {
+TEST_F(ParserImplTest, GlobalLetDecl_InvalidExpression) {
     auto p = parser("let a : f32 = if (a) {}");
     auto attrs = p->attribute_list();
     EXPECT_FALSE(attrs.errored);
@@ -77,10 +77,13 @@
     EXPECT_TRUE(e.errored);
     EXPECT_FALSE(e.matched);
     EXPECT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:15: invalid type for const_expr");
+    EXPECT_EQ(
+        p->error(),
+        R"(1:1: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+1:15: missing initializer for 'let' declaration)");
 }
 
-TEST_F(ParserImplTest, GlobalConstantDecl_MissingExpression) {
+TEST_F(ParserImplTest, GlobalLetDecl_MissingExpression) {
     auto p = parser("let a : f32 =");
     auto attrs = p->attribute_list();
     EXPECT_FALSE(attrs.errored);
@@ -90,10 +93,88 @@
     EXPECT_TRUE(e.errored);
     EXPECT_FALSE(e.matched);
     EXPECT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:14: unable to parse const_expr");
+    EXPECT_EQ(
+        p->error(),
+        R"(1:1: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+1:14: missing initializer for 'let' declaration)");
 }
 
-TEST_F(ParserImplTest, GlobalConstantDec_Override_WithId) {
+TEST_F(ParserImplTest, GlobalConstDecl) {
+    auto p = parser("const a : f32 = 1.");
+    auto attrs = p->attribute_list();
+    EXPECT_FALSE(attrs.errored);
+    EXPECT_FALSE(attrs.matched);
+    auto e = p->global_constant_decl(attrs.value);
+    EXPECT_FALSE(p->has_error()) << p->error();
+    EXPECT_TRUE(e.matched);
+    EXPECT_FALSE(e.errored);
+    auto* c = e.value->As<ast::Const>();
+    ASSERT_NE(c, nullptr);
+
+    EXPECT_EQ(c->symbol, p->builder().Symbols().Get("a"));
+    ASSERT_NE(c->type, nullptr);
+    EXPECT_TRUE(c->type->Is<ast::F32>());
+
+    EXPECT_EQ(c->source.range.begin.line, 1u);
+    EXPECT_EQ(c->source.range.begin.column, 7u);
+    EXPECT_EQ(c->source.range.end.line, 1u);
+    EXPECT_EQ(c->source.range.end.column, 8u);
+
+    ASSERT_NE(c->constructor, nullptr);
+    EXPECT_TRUE(c->constructor->Is<ast::LiteralExpression>());
+}
+
+TEST_F(ParserImplTest, GlobalConstDecl_Inferred) {
+    auto p = parser("const a = 1.");
+    auto attrs = p->attribute_list();
+    EXPECT_FALSE(attrs.errored);
+    EXPECT_FALSE(attrs.matched);
+    auto e = p->global_constant_decl(attrs.value);
+    EXPECT_FALSE(p->has_error()) << p->error();
+    EXPECT_TRUE(e.matched);
+    EXPECT_FALSE(e.errored);
+    auto* c = e.value->As<ast::Const>();
+    ASSERT_NE(c, nullptr);
+
+    EXPECT_EQ(c->symbol, p->builder().Symbols().Get("a"));
+    EXPECT_EQ(c->type, nullptr);
+
+    EXPECT_EQ(c->source.range.begin.line, 1u);
+    EXPECT_EQ(c->source.range.begin.column, 7u);
+    EXPECT_EQ(c->source.range.end.line, 1u);
+    EXPECT_EQ(c->source.range.end.column, 8u);
+
+    ASSERT_NE(c->constructor, nullptr);
+    EXPECT_TRUE(c->constructor->Is<ast::LiteralExpression>());
+}
+
+TEST_F(ParserImplTest, GlobalConstDecl_InvalidExpression) {
+    auto p = parser("const a : f32 = if (a) {}");
+    auto attrs = p->attribute_list();
+    EXPECT_FALSE(attrs.errored);
+    EXPECT_FALSE(attrs.matched);
+    auto e = p->global_constant_decl(attrs.value);
+    EXPECT_TRUE(p->has_error());
+    EXPECT_TRUE(e.errored);
+    EXPECT_FALSE(e.matched);
+    EXPECT_EQ(e.value, nullptr);
+    EXPECT_EQ(p->error(), "1:17: missing initializer for 'const' declaration");
+}
+
+TEST_F(ParserImplTest, GlobalConstDecl_MissingExpression) {
+    auto p = parser("const a : f32 =");
+    auto attrs = p->attribute_list();
+    EXPECT_FALSE(attrs.errored);
+    EXPECT_FALSE(attrs.matched);
+    auto e = p->global_constant_decl(attrs.value);
+    EXPECT_TRUE(p->has_error());
+    EXPECT_TRUE(e.errored);
+    EXPECT_FALSE(e.matched);
+    EXPECT_EQ(e.value, nullptr);
+    EXPECT_EQ(p->error(), "1:16: missing initializer for 'const' declaration");
+}
+
+TEST_F(ParserImplTest, GlobalOverrideDecl_WithId) {
     auto p = parser("@id(7) override a : f32 = 1.");
     auto attrs = p->attribute_list();
     EXPECT_FALSE(attrs.errored);
@@ -123,7 +204,7 @@
     EXPECT_EQ(override_attr->value, 7u);
 }
 
-TEST_F(ParserImplTest, GlobalConstantDec_Override_WithoutId) {
+TEST_F(ParserImplTest, GlobalOverrideDecl_WithoutId) {
     auto p = parser("override a : f32 = 1.");
     auto attrs = p->attribute_list();
     EXPECT_FALSE(attrs.errored);
@@ -152,7 +233,7 @@
     ASSERT_EQ(id_attr, nullptr);
 }
 
-TEST_F(ParserImplTest, GlobalConstantDec_Override_MissingId) {
+TEST_F(ParserImplTest, GlobalOverrideDecl_MissingId) {
     auto p = parser("@id() override a : f32 = 1.");
     auto attrs = p->attribute_list();
     EXPECT_TRUE(attrs.errored);
@@ -168,7 +249,7 @@
     EXPECT_EQ(p->error(), "1:5: expected signed integer literal for id attribute");
 }
 
-TEST_F(ParserImplTest, GlobalConstantDec_Override_InvalidId) {
+TEST_F(ParserImplTest, GlobalOverrideDecl_InvalidId) {
     auto p = parser("@id(-7) override a : f32 = 1.");
     auto attrs = p->attribute_list();
     EXPECT_TRUE(attrs.errored);
diff --git a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
index 59012a3..5abe58f 100644
--- a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
@@ -33,13 +33,20 @@
 
     auto* v = program.AST().GlobalVariables()[0];
     EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
+    EXPECT_TRUE(Is<ast::Vector>(v->type));
 }
 
-TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_Inferred_Invalid) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_Inferred) {
     auto p = parser("var<private> a = vec2<i32>(1, 2);");
     p->global_decl();
-    ASSERT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:16: expected ':' for variable declaration");
+    ASSERT_FALSE(p->has_error()) << p->error();
+
+    auto program = p->program();
+    ASSERT_EQ(program.AST().GlobalVariables().size(), 1u);
+
+    auto* v = program.AST().GlobalVariables()[0];
+    EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
+    EXPECT_EQ(v->type, nullptr);
 }
 
 TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_MissingSemicolon) {
@@ -49,7 +56,7 @@
     EXPECT_EQ(p->error(), "1:27: expected ';' for variable declaration");
 }
 
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet) {
     auto p = parser("let a : i32 = 2;");
     p->global_decl();
     ASSERT_FALSE(p->has_error()) << p->error();
@@ -61,25 +68,67 @@
     EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
 }
 
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingInitializer) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet_MissingInitializer) {
     auto p = parser("let a : vec2<i32>;");
     p->global_decl();
     ASSERT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:18: expected '=' for 'let' declaration");
+    EXPECT_EQ(
+        p->error(),
+        R"(1:1: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+1:18: expected '=' for 'let' declaration)");
 }
 
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_Invalid) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet_Invalid) {
     auto p = parser("let a : vec2<i32> 1.0;");
     p->global_decl();
     ASSERT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:19: expected '=' for 'let' declaration");
+    EXPECT_EQ(
+        p->error(),
+        R"(1:1: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+1:19: expected '=' for 'let' declaration)");
 }
 
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingSemicolon) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet_MissingSemicolon) {
     auto p = parser("let a : vec2<i32> = vec2<i32>(1, 2)");
     p->global_decl();
     ASSERT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:36: expected ';' for 'let' declaration");
+    EXPECT_EQ(
+        p->error(),
+        R"(1:1: use of deprecated language feature: module-scope 'let' has been replaced with 'const'
+1:36: expected ';' for 'const' declaration)");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst) {
+    auto p = parser("const a : i32 = 2;");
+    p->global_decl();
+    ASSERT_FALSE(p->has_error()) << p->error();
+
+    auto program = p->program();
+    ASSERT_EQ(program.AST().GlobalVariables().size(), 1u);
+
+    auto* v = program.AST().GlobalVariables()[0];
+    EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst_MissingInitializer) {
+    auto p = parser("const a : vec2<i32>;");
+    p->global_decl();
+    ASSERT_TRUE(p->has_error());
+    EXPECT_EQ(p->error(), "1:20: expected '=' for 'const' declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst_Invalid) {
+    auto p = parser("const a : vec2<i32> 1.0;");
+    p->global_decl();
+    ASSERT_TRUE(p->has_error());
+    EXPECT_EQ(p->error(), "1:21: expected '=' for 'const' declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst_MissingSemicolon) {
+    auto p = parser("const a : vec2<i32> = vec2<i32>(1, 2)");
+    p->global_decl();
+    ASSERT_TRUE(p->has_error());
+    EXPECT_EQ(p->error(), "1:38: expected ';' for 'const' declaration");
 }
 
 TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
diff --git a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
index 11371e6..f671042 100644
--- a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
@@ -152,7 +152,7 @@
     EXPECT_TRUE(e.errored);
     EXPECT_FALSE(e.matched);
     EXPECT_EQ(e.value, nullptr);
-    EXPECT_EQ(p->error(), "1:24: invalid type for const_expr");
+    EXPECT_EQ(p->error(), "1:24: missing initalizer for 'var' declaration");
 }
 
 TEST_F(ParserImplTest, GlobalVariableDecl_InvalidVariableDecl) {
diff --git a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
index 8da2771..cb61eed 100644
--- a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
@@ -25,12 +25,12 @@
     EXPECT_TRUE(p->has_error());
     EXPECT_EQ(p->error(), "1:4: '" + name + "' is a reserved keyword");
 }
-TEST_P(ParserImplReservedKeywordTest, ModuleLet) {
+TEST_P(ParserImplReservedKeywordTest, ModuleConst) {
     auto name = GetParam();
-    auto p = parser("let " + name + " : i32 = 1;");
+    auto p = parser("const " + name + " : i32 = 1;");
     EXPECT_FALSE(p->Parse());
     EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:5: '" + name + "' is a reserved keyword");
+    EXPECT_EQ(p->error(), "1:7: '" + name + "' is a reserved keyword");
 }
 TEST_P(ParserImplReservedKeywordTest, ModuleVar) {
     auto name = GetParam();
@@ -85,7 +85,6 @@
                          ParserImplReservedKeywordTest,
                          testing::Values("asm",
                                          "bf16",
-                                         "const",
                                          "do",
                                          "enum",
                                          "f64",
diff --git a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
index 70ec394..39f8b24 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -51,7 +51,7 @@
 
 TEST_F(ParserImplTest, VariableDecl_Inferred_Parses) {
     auto p = parser("var my_var = 1.0");
-    auto v = p->variable_decl(/*allow_inferred = */ true);
+    auto v = p->variable_decl();
     EXPECT_FALSE(p->has_error());
     EXPECT_TRUE(v.matched);
     EXPECT_FALSE(v.errored);
@@ -72,15 +72,6 @@
     ASSERT_TRUE(t.IsIdentifier());
 }
 
-TEST_F(ParserImplTest, VariableDecl_InvalidIdentDecl) {
-    auto p = parser("var my_var f32");
-    auto v = p->variable_decl();
-    EXPECT_FALSE(v.matched);
-    EXPECT_TRUE(v.errored);
-    EXPECT_TRUE(p->has_error());
-    EXPECT_EQ(p->error(), "1:12: expected ':' for variable declaration");
-}
-
 TEST_F(ParserImplTest, VariableDecl_WithStorageClass) {
     auto p = parser("var<private> my_var : f32");
     auto v = p->variable_decl();
diff --git a/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
index 3ffb29e..a811a82 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -19,7 +19,7 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
     auto p = parser("my_var : f32");
-    auto decl = p->expect_variable_ident_decl("test");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ false);
     ASSERT_FALSE(p->has_error()) << p->error();
     ASSERT_FALSE(decl.errored);
     ASSERT_EQ(decl->name, "my_var");
@@ -30,7 +30,27 @@
     EXPECT_EQ(decl->type->source.range, (Source::Range{{1u, 10u}, {1u, 13u}}));
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_Inferred_Parses) {
+TEST_F(ParserImplTest, VariableIdentDecl_Parses_AllowInferredType) {
+    auto p = parser("my_var : f32");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ true);
+    ASSERT_FALSE(p->has_error()) << p->error();
+    ASSERT_FALSE(decl.errored);
+    ASSERT_EQ(decl->name, "my_var");
+    ASSERT_NE(decl->type, nullptr);
+    ASSERT_TRUE(decl->type->Is<ast::F32>());
+
+    EXPECT_EQ(decl->source.range, (Source::Range{{1u, 1u}, {1u, 7u}}));
+    EXPECT_EQ(decl->type->source.range, (Source::Range{{1u, 10u}, {1u, 13u}}));
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_Inferred_Parse_Failure) {
+    auto p = parser("my_var = 1.0");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ false);
+    ASSERT_TRUE(p->has_error());
+    ASSERT_EQ(p->error(), "1:8: expected ':' for test");
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_Inferred_Parses_AllowInferredType) {
     auto p = parser("my_var = 1.0");
     auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ true);
     ASSERT_FALSE(p->has_error()) << p->error();
@@ -43,23 +63,31 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
     auto p = parser(": f32");
-    auto decl = p->expect_variable_ident_decl("test");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ false);
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
     ASSERT_EQ(p->error(), "1:1: expected identifier for test");
 }
 
-TEST_F(ParserImplTest, VariableIdentDecl_MissingColon) {
-    auto p = parser("my_var f32");
-    auto decl = p->expect_variable_ident_decl("test");
+TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent_AllowInferredType) {
+    auto p = parser(": f32");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ true);
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
-    ASSERT_EQ(p->error(), "1:8: expected ':' for test");
+    ASSERT_EQ(p->error(), "1:1: expected identifier for test");
 }
 
 TEST_F(ParserImplTest, VariableIdentDecl_MissingType) {
     auto p = parser("my_var :");
-    auto decl = p->expect_variable_ident_decl("test");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ false);
+    ASSERT_TRUE(p->has_error());
+    ASSERT_TRUE(decl.errored);
+    ASSERT_EQ(p->error(), "1:9: invalid type for test");
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_MissingType_AllowInferredType) {
+    auto p = parser("my_var :");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ true);
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
     ASSERT_EQ(p->error(), "1:9: invalid type for test");
@@ -67,7 +95,15 @@
 
 TEST_F(ParserImplTest, VariableIdentDecl_InvalidIdent) {
     auto p = parser("123 : f32");
-    auto decl = p->expect_variable_ident_decl("test");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ false);
+    ASSERT_TRUE(p->has_error());
+    ASSERT_TRUE(decl.errored);
+    ASSERT_EQ(p->error(), "1:1: expected identifier for test");
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_InvalidIdent_AllowInferredType) {
+    auto p = parser("123 : f32");
+    auto decl = p->expect_variable_ident_decl("test", /*allow_inferred = */ true);
     ASSERT_TRUE(p->has_error());
     ASSERT_TRUE(decl.errored);
     ASSERT_EQ(p->error(), "1:1: expected identifier for test");
diff --git a/src/tint/reader/wgsl/token.cc b/src/tint/reader/wgsl/token.cc
index aec1c9a..c6a06c9 100644
--- a/src/tint/reader/wgsl/token.cc
+++ b/src/tint/reader/wgsl/token.cc
@@ -141,6 +141,8 @@
             return "break";
         case Token::Type::kCase:
             return "case";
+        case Token::Type::kConst:
+            return "const";
         case Token::Type::kContinue:
             return "continue";
         case Token::Type::kContinuing:
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index 2fcf742..df78a5b 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -151,6 +151,8 @@
         kBreak,
         /// A 'case'
         kCase,
+        /// A 'const'
+        kConst,
         /// A 'continue'
         kContinue,
         /// A 'continuing'
@@ -349,7 +351,8 @@
     bool IsLiteral() const {
         return type_ == Type::kIntLiteral || type_ == Type::kIntLiteral_I ||
                type_ == Type::kIntLiteral_U || type_ == Type::kFalse || type_ == Type::kTrue ||
-               type_ == Type::kFloatLiteral || type_ == Type::kFloatLiteral_F;
+               type_ == Type::kFloatLiteral || type_ == Type::kFloatLiteral_F ||
+               type_ == Type::kFloatLiteral_H;
     }
     /// @returns true if token is a 'matNxM'
     bool IsMatrix() const {
diff --git a/src/tint/reader/wgsl/token_test.cc b/src/tint/reader/wgsl/token_test.cc
index 93fe834..fd4515f 100644
--- a/src/tint/reader/wgsl/token_test.cc
+++ b/src/tint/reader/wgsl/token_test.cc
@@ -85,6 +85,8 @@
     EXPECT_THAT(Token(Token::Type::kFloatLiteral, Source{}, d).to_str(), Not(EndsWith("f")));
     EXPECT_THAT(Token(Token::Type::kFloatLiteral_F, Source{}, d).to_str(), StartsWith("123"));
     EXPECT_THAT(Token(Token::Type::kFloatLiteral_F, Source{}, d).to_str(), EndsWith("f"));
+    EXPECT_THAT(Token(Token::Type::kFloatLiteral_H, Source{}, d).to_str(), StartsWith("123"));
+    EXPECT_THAT(Token(Token::Type::kFloatLiteral_H, Source{}, d).to_str(), EndsWith("h"));
     EXPECT_EQ(Token(Token::Type::kIntLiteral, Source{}, i).to_str(), "123");
     EXPECT_EQ(Token(Token::Type::kIntLiteral_I, Source{}, i).to_str(), "123i");
     EXPECT_EQ(Token(Token::Type::kIntLiteral_U, Source{}, i).to_str(), "123u");
diff --git a/src/tint/resolver/array_accessor_test.cc b/src/tint/resolver/array_accessor_test.cc
index 535d1ff..be49546 100644
--- a/src/tint/resolver/array_accessor_test.cc
+++ b/src/tint/resolver/array_accessor_test.cc
@@ -16,6 +16,7 @@
 
 #include "gmock/gmock.h"
 #include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/index_accessor_expression.h"
 #include "src/tint/sem/reference.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -26,7 +27,7 @@
 using ResolverIndexAccessorTest = ResolverTest;
 
 TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_F32) {
-    Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
     auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 1_f));
     WrapInFunction(acc);
 
@@ -35,22 +36,32 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_Ref) {
-    Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
     auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
     auto* acc = IndexAccessor("my_var", idx);
     WrapInFunction(Decl(idx), acc);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions_Dynamic_Ref) {
-    Global("my_var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
     auto* idx = Var("idx", ty.u32(), Expr(3_u));
     auto* idy = Var("idy", ty.u32(), Expr(2_u));
     auto* acc = IndexAccessor(IndexAccessor("my_var", idx), idy);
     WrapInFunction(Decl(idx), Decl(idy), acc);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic) {
@@ -61,12 +72,17 @@
 
     EXPECT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "");
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_XDimension_Dynamic) {
-    GlobalConst("my_var", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
+    GlobalConst("my_const", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
     auto* idx = Var("idx", ty.u32(), Expr(3_u));
-    auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
+    auto* acc = IndexAccessor("my_const", Expr(Source{{12, 34}}, idx));
     WrapInFunction(Decl(idx), acc);
 
     EXPECT_TRUE(r()->Resolve());
@@ -74,9 +90,9 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_BothDimension_Dynamic) {
-    GlobalConst("my_var", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
+    GlobalConst("my_const", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
     auto* idx = Var("idy", ty.u32(), Expr(2_u));
-    auto* acc = IndexAccessor(IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)), 1_i);
+    auto* acc = IndexAccessor(IndexAccessor("my_const", Expr(Source{{12, 34}}, idx)), 1_i);
     WrapInFunction(Decl(idx), acc);
 
     EXPECT_TRUE(r()->Resolve());
@@ -84,7 +100,7 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix) {
-    Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
@@ -97,10 +113,15 @@
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     ASSERT_TRUE(ref->StoreType()->Is<sem::Vector>());
     EXPECT_EQ(ref->StoreType()->As<sem::Vector>()->Width(), 3u);
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions) {
-    Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* acc = IndexAccessor(IndexAccessor("my_var", 2_i), 1_i);
     WrapInFunction(acc);
@@ -112,10 +133,15 @@
 
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector_F32) {
-    Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
     auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 2_f));
     WrapInFunction(acc);
 
@@ -124,25 +150,30 @@
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector_Dynamic_Ref) {
-    Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
     auto* idx = Var("idx", ty.i32(), Expr(2_i));
     auto* acc = IndexAccessor("my_var", idx);
     WrapInFunction(Decl(idx), acc);
 
     EXPECT_TRUE(r()->Resolve());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector_Dynamic) {
-    GlobalConst("my_var", ty.vec3<f32>(), Construct(ty.vec3<f32>()));
+    GlobalConst("my_const", ty.vec3<f32>(), Construct(ty.vec3<f32>()));
     auto* idx = Var("idx", ty.i32(), Expr(2_i));
-    auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
+    auto* acc = IndexAccessor("my_const", Expr(Source{{12, 34}}, idx));
     WrapInFunction(Decl(idx), acc);
 
     EXPECT_TRUE(r()->Resolve());
 }
 
 TEST_F(ResolverIndexAccessorTest, Vector) {
-    Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
@@ -154,10 +185,15 @@
 
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_i32) {
-    Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -165,10 +201,15 @@
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     ASSERT_NE(ref, nullptr);
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_u32) {
-    Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
     auto* acc = IndexAccessor("my_var", 2_u);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -176,10 +217,15 @@
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     ASSERT_NE(ref, nullptr);
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_AInt) {
-    Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
     auto* acc = IndexAccessor("my_var", 2_a);
     WrapInFunction(acc);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -187,12 +233,17 @@
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     ASSERT_NE(ref, nullptr);
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Alias_Array) {
     auto* aary = Alias("myarrty", ty.array<f32, 3>());
 
-    Global("my_var", ty.Of(aary), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.Of(aary), ast::StorageClass::kPrivate);
 
     auto* acc = IndexAccessor("my_var", 2_i);
     WrapInFunction(acc);
@@ -204,12 +255,17 @@
 
     auto* ref = TypeOf(acc)->As<sem::Reference>();
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Constant) {
-    GlobalConst("my_var", ty.array<f32, 3>(), array<f32, 3>());
+    GlobalConst("my_const", ty.array<f32, 3>(), array<f32, 3>());
 
-    auto* acc = IndexAccessor("my_var", 2_i);
+    auto* acc = IndexAccessor("my_const", 2_i);
     WrapInFunction(acc);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -224,7 +280,8 @@
     // var f : f32 = a[idx];
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
     auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
-    auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
+    auto* acc = IndexAccessor("a", Expr(Source{{12, 34}}, idx));
+    auto* f = Var("f", ty.f32(), acc);
     Func("my_func", {}, ty.void_(),
          {
              Decl(a),
@@ -234,6 +291,11 @@
 
     EXPECT_TRUE(r()->Resolve());
     EXPECT_EQ(r()->error(), "");
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Array_Literal_F32) {
@@ -254,13 +316,19 @@
     // let a : array<f32, 3>;
     // var f : f32 = a[2i];
     auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
-    auto* f = Var("a_2", ty.f32(), IndexAccessor("a", 2_i));
+    auto* acc = IndexAccessor("a", 2_i);
+    auto* f = Var("a_2", ty.f32(), acc);
     Func("my_func", {}, ty.void_(),
          {
              Decl(a),
              Decl(f),
          });
     EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Expr_Deref_FuncGoodParent) {
@@ -272,11 +340,16 @@
     auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
     auto* idx = Let("idx", ty.u32(), Construct(ty.u32()));
     auto* star_p = Deref(p);
-    auto* accessor_expr = IndexAccessor(Source{{12, 34}}, star_p, idx);
-    auto* x = Var("x", ty.f32(), accessor_expr);
+    auto* acc = IndexAccessor(Source{{12, 34}}, star_p, idx);
+    auto* x = Var("x", ty.f32(), acc);
     Func("func", {p}, ty.f32(), {Decl(idx), Decl(x), Return(x)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto idx_sem = Sem().Get(acc);
+    ASSERT_NE(idx_sem, nullptr);
+    EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
+    EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
 }
 
 TEST_F(ResolverIndexAccessorTest, Expr_Deref_FuncBadParent) {
diff --git a/src/tint/resolver/assignment_validation_test.cc b/src/tint/resolver/assignment_validation_test.cc
index 6af636e..e204a62 100644
--- a/src/tint/resolver/assignment_validation_test.cc
+++ b/src/tint/resolver/assignment_validation_test.cc
@@ -30,11 +30,11 @@
     // @group(0) @binding(0)
     // var<storage,read> a : S;
     auto* s = Structure("S", {Member("m", ty.i32())});
-    Global(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("a", "m"), 1_i));
 
@@ -61,7 +61,7 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignArraysWithDifferentSizeExpressions_Pass) {
-    // let len = 4u;
+    // const len = 4u;
     // {
     //   var a : array<f32, 4u>;
     //   var b : array<f32, len>;
@@ -80,7 +80,7 @@
 }
 
 TEST_F(ResolverAssignmentValidationTest, AssignArraysWithDifferentSizeExpressions_Fail) {
-    // let len = 5u;
+    // const len = 5u;
     // {
     //   var a : array<f32, 4u>;
     //   var b : array<f32, len>;
@@ -235,16 +235,16 @@
                                   ast::Access::kWrite);
     };
 
-    Global("a", make_type(), ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
-    Global("b", make_type(), ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("a", make_type(), ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+    GlobalVar("b", make_type(), ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     WrapInFunction(Assign(Source{{56, 78}}, "a", "b"));
 
@@ -258,11 +258,11 @@
     // v.a = v.a;
 
     auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))});
-    Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"), MemberAccessor("v", "a")));
 
@@ -276,11 +276,11 @@
     // v.a = v.a;
 
     auto* s = Structure("S", {Member("a", ty.array(ty.f32()))});
-    Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"), MemberAccessor("v", "a")));
 
@@ -297,7 +297,7 @@
     //   _ = s;
     // }
     auto* s = Structure("S", {Member("arr", ty.array<i32>())});
-    Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
+    GlobalVar("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
 
     WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
 
@@ -317,7 +317,7 @@
     //   _ = s.arr;
     // }
     auto* s = Structure("S", {Member("arr", ty.array<i32>())});
-    Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
+    GlobalVar("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
 
     WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
 
@@ -365,11 +365,12 @@
                                  Member("arr", ty.array<i32>()),
                              });
     auto* U = Structure("U", {Member("i", ty.i32())});
-    Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(0, 0));
-    Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
-    Global("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
-    Global("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
-    Global("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              GroupAndBinding(0, 0));
+    GlobalVar("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
+    GlobalVar("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
+    GlobalVar("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
+    GlobalVar("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
 
     WrapInFunction(Assign(Phony(), 1_i),                                    //
                    Assign(Phony(), 2_u),                                    //
diff --git a/src/tint/resolver/atomics_test.cc b/src/tint/resolver/atomics_test.cc
index c0fe5ce..09ca0ba 100644
--- a/src/tint/resolver/atomics_test.cc
+++ b/src/tint/resolver/atomics_test.cc
@@ -25,7 +25,7 @@
 struct ResolverAtomicTest : public resolver::TestHelper, public testing::Test {};
 
 TEST_F(ResolverAtomicTest, GlobalWorkgroupI32) {
-    auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
+    auto* g = GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
@@ -35,7 +35,7 @@
 }
 
 TEST_F(ResolverAtomicTest, GlobalWorkgroupU32) {
-    auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.u32()), ast::StorageClass::kWorkgroup);
+    auto* g = GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.u32()), ast::StorageClass::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
@@ -46,11 +46,11 @@
 
 TEST_F(ResolverAtomicTest, GlobalStorageStruct) {
     auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    auto* g = Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                     ast::AttributeList{
-                         create<ast::BindingAttribute>(0),
-                         create<ast::GroupAttribute>(0),
-                     });
+    auto* g = GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                        ast::AttributeList{
+                            create<ast::BindingAttribute>(0u),
+                            create<ast::GroupAttribute>(0u),
+                        });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
     ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
diff --git a/src/tint/resolver/atomics_validation_test.cc b/src/tint/resolver/atomics_validation_test.cc
index 78c1fb9..5ec4e01 100644
--- a/src/tint/resolver/atomics_validation_test.cc
+++ b/src/tint/resolver/atomics_validation_test.cc
@@ -27,35 +27,35 @@
 struct ResolverAtomicValidationTest : public resolver::TestHelper, public testing::Test {};
 
 TEST_F(ResolverAtomicValidationTest, StorageClass_WorkGroup) {
-    Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
 
     EXPECT_TRUE(r()->Resolve());
 }
 
 TEST_F(ResolverAtomicValidationTest, StorageClass_Storage) {
-    Global("g", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kStorage,
-           ast::Access::kReadWrite, GroupAndBinding(0, 0));
+    GlobalVar("g", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kStorage,
+              ast::Access::kReadWrite, GroupAndBinding(0, 0));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverAtomicValidationTest, StorageClass_Storage_Struct) {
     auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           GroupAndBinding(0, 0));
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              GroupAndBinding(0, 0));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidType) {
-    Global("a", ty.atomic(ty.f32(Source{{12, 34}})), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic(ty.f32(Source{{12, 34}})), ast::StorageClass::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types");
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Simple) {
-    Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -64,7 +64,7 @@
 }
 
 TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Array) {
-    Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -74,7 +74,7 @@
 
 TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Struct) {
     auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -90,7 +90,7 @@
 
     auto* Inner = Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
     auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
-    Global("g", ty.Of(Outer), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(Outer), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -106,7 +106,7 @@
 
     auto* Inner = Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
     auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
-    Global("g", ty.Of(Outer), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(Outer), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -121,7 +121,7 @@
 
     auto* atomic_array =
         Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
-    Global(Source{{56, 78}}, "v", ty.Of(atomic_array), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "v", ty.Of(atomic_array), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -136,7 +136,7 @@
     // var<private> v: array<S, 5u>;
 
     auto* s = Structure("S", {Member("m", ty.atomic<u32>())});
-    Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -155,7 +155,7 @@
     auto* atomic_array =
         Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
     auto* s = Structure("S", {Member("m", ty.Of(atomic_array))});
-    Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -196,7 +196,7 @@
     auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
     auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
     auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
-    Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -207,8 +207,8 @@
 
 TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
     auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -219,8 +219,8 @@
 
 TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
     auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
-    Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -236,8 +236,8 @@
 
     auto* Inner = Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
     auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
-    Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -253,8 +253,8 @@
 
     auto* Inner = Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
     auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
-    Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -295,8 +295,8 @@
     auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
     auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
     auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
-    Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -309,7 +309,7 @@
     WrapInFunction(Var("a", ty.atomic(Source{{12, 34}}, ty.i32())));
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: function variable must have a constructible type");
+    EXPECT_EQ(r()->error(), "12:34 error: function-scope 'var' must have a constructible type");
 }
 
 }  // namespace
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index f7e401b..5f69277 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -508,8 +508,8 @@
 TEST_F(EntryPointParameterAttributeTest, DuplicateInternalAttribute) {
     auto* s = Param("s", ty.sampler(ast::SamplerKind::kSampler),
                     ast::AttributeList{
-                        create<ast::BindingAttribute>(0),
-                        create<ast::GroupAttribute>(0),
+                        create<ast::BindingAttribute>(0u),
+                        create<ast::GroupAttribute>(0u),
                         Disable(ast::DisabledValidation::kBindingPointCollision),
                         Disable(ast::DisabledValidation::kEntryPointParameter),
                     });
@@ -707,11 +707,11 @@
     auto& params = GetParam();
 
     if (IsBindingAttribute(params.kind)) {
-        Global("a", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
-               createAttributes(Source{{12, 34}}, *this, params.kind));
+        GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
+                  createAttributes(Source{{12, 34}}, *this, params.kind));
     } else {
-        Global("a", ty.f32(), ast::StorageClass::kPrivate, nullptr,
-               createAttributes(Source{{12, 34}}, *this, params.kind));
+        GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, nullptr,
+                  createAttributes(Source{{12, 34}}, *this, params.kind));
     }
 
     WrapInFunction();
@@ -743,12 +743,12 @@
                                          TestParams{AttributeKind::kBindingAndGroup, true}));
 
 TEST_F(VariableAttributeTest, DuplicateAttribute) {
-    Global("a", ty.sampler(ast::SamplerKind::kSampler),
-           ast::AttributeList{
-               create<ast::BindingAttribute>(Source{{12, 34}}, 2),
-               create<ast::GroupAttribute>(2),
-               create<ast::BindingAttribute>(Source{{56, 78}}, 3),
-           });
+    GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler),
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(Source{{12, 34}}, 2u),
+                  create<ast::GroupAttribute>(2u),
+                  create<ast::BindingAttribute>(Source{{56, 78}}, 3u),
+              });
 
     WrapInFunction();
 
@@ -761,7 +761,7 @@
 TEST_F(VariableAttributeTest, LocalVariable) {
     auto* v = Var("a", ty.f32(),
                   ast::AttributeList{
-                      create<ast::BindingAttribute>(Source{{12, 34}}, 2),
+                      create<ast::BindingAttribute>(Source{{12, 34}}, 2u),
                   });
 
     WrapInFunction(v);
@@ -784,7 +784,7 @@
     } else {
         EXPECT_FALSE(r()->Resolve());
         EXPECT_EQ(r()->error(),
-                  "12:34 error: attribute is not valid for module-scope 'let' declaration");
+                  "12:34 error: attribute is not valid for module-scope 'const' declaration");
     }
 }
 INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
@@ -807,8 +807,8 @@
 TEST_F(ConstantAttributeTest, DuplicateAttribute) {
     GlobalConst("a", ty.f32(), Expr(1.23_f),
                 ast::AttributeList{
-                    create<ast::IdAttribute>(Source{{12, 34}}, 0),
-                    create<ast::IdAttribute>(Source{{56, 78}}, 1),
+                    create<ast::IdAttribute>(Source{{12, 34}}, 0u),
+                    create<ast::IdAttribute>(Source{{56, 78}}, 1u),
                 });
 
     WrapInFunction();
@@ -852,11 +852,11 @@
                                          TestParams{AttributeKind::kBindingAndGroup, false}));
 
 TEST_F(OverrideAttributeTest, DuplicateAttribute) {
-    GlobalConst("a", ty.f32(), Expr(1.23_f),
-                ast::AttributeList{
-                    create<ast::IdAttribute>(Source{{12, 34}}, 0),
-                    create<ast::IdAttribute>(Source{{56, 78}}, 1),
-                });
+    Override("a", ty.f32(), Expr(1.23_f),
+             ast::AttributeList{
+                 create<ast::IdAttribute>(Source{{12, 34}}, 0u),
+                 create<ast::IdAttribute>(Source{{56, 78}}, 1u),
+             });
 
     WrapInFunction();
 
@@ -897,7 +897,7 @@
 
     auto* arr = ty.array(Source{{12, 34}}, el_ty, 4_u, params.stride);
 
-    Global("myarray", arr, ast::StorageClass::kPrivate);
+    GlobalVar("myarray", arr, ast::StorageClass::kPrivate);
 
     if (params.should_pass) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -976,11 +976,11 @@
 TEST_F(ArrayStrideTest, DuplicateAttribute) {
     auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4_u,
                          {
-                             create<ast::StrideAttribute>(Source{{12, 34}}, 4_i),
-                             create<ast::StrideAttribute>(Source{{56, 78}}, 4_i),
+                             create<ast::StrideAttribute>(Source{{12, 34}}, 4u),
+                             create<ast::StrideAttribute>(Source{{56, 78}}, 4u),
                          });
 
-    Global("myarray", arr, ast::StorageClass::kPrivate);
+    GlobalVar("myarray", arr, ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -997,7 +997,7 @@
 using ResourceAttributeTest = ResolverTest;
 TEST_F(ResourceAttributeTest, UniformBufferMissingBinding) {
     auto* s = Structure("S", {Member("x", ty.i32())});
-    Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
+    GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1006,7 +1006,7 @@
 
 TEST_F(ResourceAttributeTest, StorageBufferMissingBinding) {
     auto* s = Structure("S", {Member("x", ty.i32())});
-    Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead);
+    GlobalVar(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1014,8 +1014,8 @@
 }
 
 TEST_F(ResourceAttributeTest, TextureMissingBinding) {
-    Global(Source{{12, 34}}, "G", ty.depth_texture(ast::TextureDimension::k2d),
-           ast::StorageClass::kNone);
+    GlobalVar(Source{{12, 34}}, "G", ty.depth_texture(ast::TextureDimension::k2d),
+              ast::StorageClass::kNone);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1023,7 +1023,8 @@
 }
 
 TEST_F(ResourceAttributeTest, SamplerMissingBinding) {
-    Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone);
+    GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
+              ast::StorageClass::kNone);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1031,10 +1032,11 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPairMissingBinding) {
-    Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
+              ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::GroupAttribute>(1u),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1042,10 +1044,11 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPairMissingGroup) {
-    Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-           });
+    GlobalVar(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
+              ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -1053,18 +1056,18 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPointUsedTwiceByEntryPoint) {
-    Global(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-           ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
-    Global(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-           ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
+    GlobalVar(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("F", {}, ty.void_(),
          {
@@ -1085,18 +1088,18 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPointUsedTwiceByDifferentEntryPoints) {
-    Global(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-           ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
-    Global(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-           ast::StorageClass::kNone,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
+    GlobalVar(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              ast::StorageClass::kNone,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("F_A", {}, ty.void_(),
          {
@@ -1119,11 +1122,11 @@
 }
 
 TEST_F(ResourceAttributeTest, BindingPointOnNonResource) {
-    Global(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index ef9f1bd..38f5620 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -52,7 +52,7 @@
 TEST_P(ResolverBuiltinDerivativeTest, Scalar) {
     auto name = GetParam();
 
-    Global("ident", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* expr = Call(name, "ident");
     Func("func", {}, ty.void_(), {Ignore(expr)},
@@ -66,7 +66,7 @@
 
 TEST_P(ResolverBuiltinDerivativeTest, Vector) {
     auto name = GetParam();
-    Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call(name, "ident");
     Func("func", {}, ty.void_(), {Ignore(expr)},
@@ -110,7 +110,7 @@
 TEST_P(ResolverBuiltinTest_BoolMethod, Scalar) {
     auto name = GetParam();
 
-    Global("my_var", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = Call(name, "my_var");
     WrapInFunction(expr);
@@ -123,7 +123,7 @@
 TEST_P(ResolverBuiltinTest_BoolMethod, Vector) {
     auto name = GetParam();
 
-    Global("my_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call(name, "my_var");
     WrapInFunction(expr);
@@ -185,14 +185,14 @@
 
     void add_call_param(std::string name, const ast::Type* type, ast::ExpressionList* call_params) {
         if (type->IsAnyOf<ast::Texture, ast::Sampler>()) {
-            Global(name, type,
-                   ast::AttributeList{
-                       create<ast::BindingAttribute>(0),
-                       create<ast::GroupAttribute>(0),
-                   });
+            GlobalVar(name, type,
+                      ast::AttributeList{
+                          create<ast::BindingAttribute>(0u),
+                          create<ast::GroupAttribute>(0u),
+                      });
 
         } else {
-            Global(name, type, ast::StorageClass::kPrivate);
+            GlobalVar(name, type, ast::StorageClass::kPrivate);
         }
 
         call_params->push_back(Expr(name));
@@ -251,7 +251,7 @@
                                          TextureTestParams{ast::TextureDimension::k3d}));
 
 TEST_F(ResolverBuiltinTest, Dot_Vec2) {
-    Global("my_var", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec2<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -263,7 +263,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Dot_Vec3) {
-    Global("my_var", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<i32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -275,7 +275,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Dot_Vec4) {
-    Global("my_var", ty.vec4<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec4<u32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("dot", "my_var", "my_var");
     WrapInFunction(expr);
@@ -301,9 +301,9 @@
 }
 
 TEST_F(ResolverBuiltinTest, Select) {
-    Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
-    Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("bool_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("select", "my_var", "my_var", "bool_var");
     WrapInFunction(expr);
@@ -616,11 +616,11 @@
 TEST_F(ResolverBuiltinDataTest, ArrayLength_Vector) {
     auto* ary = ty.array<i32>();
     auto* str = Structure("S", {Member("x", ary)});
-    Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     auto* call = Call("arrayLength", AddressOf(MemberAccessor("a", "x")));
     WrapInFunction(call);
@@ -632,7 +632,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, ArrayLength_Error_ArraySized) {
-    Global("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
+    GlobalVar("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
     auto* call = Call("arrayLength", AddressOf("arr"));
     WrapInFunction(call);
 
@@ -733,7 +733,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, Frexp_Error_FirstParamInt) {
-    Global("v", ty.i32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("v", ty.i32(), ast::StorageClass::kWorkgroup);
     auto* call = Call("frexp", 1_i, AddressOf("v"));
     WrapInFunction(call);
 
@@ -749,7 +749,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, Frexp_Error_SecondParamFloatPtr) {
-    Global("v", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("v", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* call = Call("frexp", 1_f, AddressOf("v"));
     WrapInFunction(call);
 
@@ -779,7 +779,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, Frexp_Error_VectorSizesDontMatch) {
-    Global("v", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("v", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
     auto* call = Call("frexp", vec2<f32>(1_f, 2_f), AddressOf("v"));
     WrapInFunction(call);
 
@@ -857,7 +857,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, Modf_Error_FirstParamInt) {
-    Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("whole", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* call = Call("modf", 1_i, AddressOf("whole"));
     WrapInFunction(call);
 
@@ -873,7 +873,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, Modf_Error_SecondParamIntPtr) {
-    Global("whole", ty.i32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("whole", ty.i32(), ast::StorageClass::kWorkgroup);
     auto* call = Call("modf", 1_f, AddressOf("whole"));
     WrapInFunction(call);
 
@@ -903,7 +903,7 @@
 }
 
 TEST_F(ResolverBuiltinDataTest, Modf_Error_VectorSizesDontMatch) {
-    Global("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
     auto* call = Call("modf", vec2<f32>(1_f, 2_f), AddressOf("whole"));
     WrapInFunction(call);
 
@@ -1512,7 +1512,7 @@
                                          BuiltinData{"max", BuiltinType::kMax}));
 
 TEST_F(ResolverBuiltinTest, Determinant_2x2) {
-    Global("var", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1524,7 +1524,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_3x3) {
-    Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1536,7 +1536,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_4x4) {
-    Global("var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1548,7 +1548,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_NotSquare) {
-    Global("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
@@ -1563,7 +1563,7 @@
 }
 
 TEST_F(ResolverBuiltinTest, Determinant_NotMatrix) {
-    Global("var", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("determinant", "var");
     WrapInFunction(call);
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index d82c2f3..3737ae0 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -85,20 +85,21 @@
               R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a function)");
 }
 
-TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalLet) {
+TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalConst) {
     GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope let)");
+              R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a 'const')");
 }
 
 TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVar) {
-    Global(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "mix", ty.i32(), Expr(1_i), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope var)");
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope 'var')");
 }
 
 TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAlias) {
@@ -256,11 +257,11 @@
             err << "12:34 error: each component of the " << param.name
                 << " argument must be at least " << param.min << " and at most " << param.max
                 << ". " << param.name << " component " << expr.invalid_index << " is "
-                << std::to_string(expr.values[expr.invalid_index]);
+                << std::to_string(expr.values[static_cast<size_t>(expr.invalid_index)]);
         } else {
             err << "12:34 error: the " << param.name << " argument must be at least " << param.min
                 << " and at most " << param.max << ". " << param.name << " is "
-                << std::to_string(expr.values[expr.invalid_index]);
+                << std::to_string(expr.values[static_cast<size_t>(expr.invalid_index)]);
         }
         EXPECT_EQ(r()->error(), err.str());
     }
@@ -276,7 +277,7 @@
     overload.BuildTextureVariable(this);
     overload.BuildSamplerVariable(this);
 
-    // Build the module-scope let 'G' with the offset value
+    // Build the module-scope const 'G' with the offset value
     GlobalConst("G", nullptr, expr({}, *this));
 
     auto args = overload.args(this);
@@ -297,7 +298,6 @@
     err << "12:34 error: the " << param.name << " argument must be a const_expression";
     EXPECT_EQ(r()->error(), err.str());
 }
-
 INSTANTIATE_TEST_SUITE_P(
     Offset2D,
     BuiltinTextureConstExprArgValidationTest,
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index 6f0cf85..15fd2b6 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -92,7 +92,7 @@
 TEST_P(ResolverBuiltinsStageTest, All_input) {
     const Params& params = GetParam();
 
-    auto* p = Global("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    auto* p = GlobalVar("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* input = Param("input", params.type(*this), {Builtin(Source{{12, 34}}, params.builtin)});
     switch (params.stage) {
         case ast::PipelineStage::kVertex:
diff --git a/src/tint/resolver/call_test.cc b/src/tint/resolver/call_test.cc
index 5351623..3020254 100644
--- a/src/tint/resolver/call_test.cc
+++ b/src/tint/resolver/call_test.cc
@@ -71,6 +71,7 @@
     ParamsFor<u32>(),          //
     ParamsFor<i32>(),          //
     ParamsFor<f32>(),          //
+    ParamsFor<f16>(),          //
     ParamsFor<vec3<bool>>(),   //
     ParamsFor<vec3<i32>>(),    //
     ParamsFor<vec3<u32>>(),    //
@@ -81,6 +82,8 @@
 };
 
 TEST_F(ResolverCallTest, Valid) {
+    Enable(ast::Extension::kF16);
+
     ast::ParameterList params;
     ast::ExpressionList args;
     for (auto& p : all_param_types) {
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 850e2c6..9625be7 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -219,7 +219,7 @@
     //   var c: i32 = foo(p);
     // }
     Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kPrivate))}, ty.void_(), {});
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(v));
     auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("foo", Expr(Source{{12, 34}}, p)));
     Func("main", {}, ty.void_(),
@@ -241,7 +241,7 @@
     // fn f() {
     //   v();
     // }
-    Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     Func("f", {}, ty.void_(), {CallStmt(Call(Source{{12, 34}}, "v"))});
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/compound_assignment_validation_test.cc b/src/tint/resolver/compound_assignment_validation_test.cc
index b453c3b..5edcdaa 100644
--- a/src/tint/resolver/compound_assignment_validation_test.cc
+++ b/src/tint/resolver/compound_assignment_validation_test.cc
@@ -233,8 +233,8 @@
     // {
     //   a += 1i;
     // }
-    Global(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
     WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", 1_i, ast::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
@@ -264,7 +264,7 @@
 TEST_F(ResolverCompoundAssignmentValidationTest, LhsAtomic) {
     // var<workgroup> a : atomic<i32>;
     // a += a;
-    Global(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
     WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", "a", ast::BinaryOp::kAdd));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/const_eval.h b/src/tint/resolver/const_eval.h
index 89bb3da..3792e35 100644
--- a/src/tint/resolver/const_eval.h
+++ b/src/tint/resolver/const_eval.h
@@ -30,7 +30,9 @@
 namespace tint::resolver::const_eval {
 
 /// Typedef for a constant evaluation function
-using Function = sem::Constant(ProgramBuilder& builder, const sem::Constant* args, size_t num_args);
+using Function = const sem::Constant*(ProgramBuilder& builder,
+                                      sem::Constant const* const* args,
+                                      size_t num_args);
 
 }  // namespace tint::resolver::const_eval
 
diff --git a/src/tint/resolver/ctor_conv_intrinsic.cc b/src/tint/resolver/ctor_conv_intrinsic.cc
index 5618fba..59eb1c5 100644
--- a/src/tint/resolver/ctor_conv_intrinsic.cc
+++ b/src/tint/resolver/ctor_conv_intrinsic.cc
@@ -36,6 +36,8 @@
             return "u32";
         case CtorConvIntrinsic::kF32:
             return "f32";
+        case CtorConvIntrinsic::kF16:
+            return "f16";
         case CtorConvIntrinsic::kBool:
             return "bool";
         case CtorConvIntrinsic::kVec2:
@@ -67,4 +69,3 @@
 }
 
 }  // namespace tint::resolver
-
diff --git a/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl b/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl
index ac98c4d..db567ca 100644
--- a/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl
+++ b/src/tint/resolver/ctor_conv_intrinsic.cc.tmpl
@@ -25,4 +25,3 @@
 }
 
 }  // namespace tint::resolver
-
diff --git a/src/tint/resolver/ctor_conv_intrinsic.h b/src/tint/resolver/ctor_conv_intrinsic.h
index 7c68658..186cef7 100644
--- a/src/tint/resolver/ctor_conv_intrinsic.h
+++ b/src/tint/resolver/ctor_conv_intrinsic.h
@@ -36,6 +36,7 @@
     kI32,
     kU32,
     kF32,
+    kF16,
     kBool,
     kVec2,
     kVec3,
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 6b3b779..3d550ff 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -19,12 +19,58 @@
 #include <utility>
 #include <vector>
 
+#include "src/tint/ast/alias.h"
+#include "src/tint/ast/array.h"
+#include "src/tint/ast/assignment_statement.h"
+#include "src/tint/ast/atomic.h"
+#include "src/tint/ast/block_statement.h"
+#include "src/tint/ast/bool.h"
+#include "src/tint/ast/break_statement.h"
+#include "src/tint/ast/call_statement.h"
+#include "src/tint/ast/compound_assignment_statement.h"
 #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/discard_statement.h"
+#include "src/tint/ast/external_texture.h"
+#include "src/tint/ast/f16.h"
+#include "src/tint/ast/f32.h"
 #include "src/tint/ast/fallthrough_statement.h"
+#include "src/tint/ast/for_loop_statement.h"
+#include "src/tint/ast/i32.h"
+#include "src/tint/ast/id_attribute.h"
+#include "src/tint/ast/if_statement.h"
+#include "src/tint/ast/increment_decrement_statement.h"
+#include "src/tint/ast/internal_attribute.h"
+#include "src/tint/ast/interpolate_attribute.h"
+#include "src/tint/ast/invariant_attribute.h"
+#include "src/tint/ast/location_attribute.h"
+#include "src/tint/ast/loop_statement.h"
+#include "src/tint/ast/matrix.h"
+#include "src/tint/ast/multisampled_texture.h"
+#include "src/tint/ast/pointer.h"
+#include "src/tint/ast/return_statement.h"
+#include "src/tint/ast/sampled_texture.h"
+#include "src/tint/ast/stage_attribute.h"
+#include "src/tint/ast/storage_texture.h"
+#include "src/tint/ast/stride_attribute.h"
+#include "src/tint/ast/struct.h"
+#include "src/tint/ast/struct_member_align_attribute.h"
+#include "src/tint/ast/struct_member_offset_attribute.h"
+#include "src/tint/ast/struct_member_size_attribute.h"
+#include "src/tint/ast/switch_statement.h"
 #include "src/tint/ast/traverse_expressions.h"
+#include "src/tint/ast/type_name.h"
+#include "src/tint/ast/u32.h"
+#include "src/tint/ast/variable_decl_statement.h"
+#include "src/tint/ast/vector.h"
+#include "src/tint/ast/void.h"
+#include "src/tint/ast/while_statement.h"
+#include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/scope_stack.h"
 #include "src/tint/sem/builtin.h"
+#include "src/tint/symbol_table.h"
+#include "src/tint/utils/block_allocator.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
 #include "src/tint/utils/scoped_assignment.h"
@@ -487,13 +533,11 @@
     /// declaration
     std::string KindOf(const ast::Node* node) {
         return Switch(
-            node,                                              //
-            [&](const ast::Struct*) { return "struct"; },      //
-            [&](const ast::Alias*) { return "alias"; },        //
-            [&](const ast::Function*) { return "function"; },  //
-            [&](const ast::Let*) { return "let"; },            //
-            [&](const ast::Var*) { return "var"; },            //
-            [&](const ast::Override*) { return "override"; },  //
+            node,                                               //
+            [&](const ast::Struct*) { return "struct"; },       //
+            [&](const ast::Alias*) { return "alias"; },         //
+            [&](const ast::Function*) { return "function"; },   //
+            [&](const ast::Variable* v) { return v->Kind(); },  //
             [&](Default) {
                 UnhandledNode(diagnostics_, node);
                 return "<error>";
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 1117a4a..b7ffa79 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -113,12 +113,12 @@
     GlobalVarSampledTexElemType,
     GlobalVarMultisampledTexElemType,
     GlobalVarValue,
-    GlobalLetType,
-    GlobalLetArrayElemType,
-    GlobalLetArraySizeValue,
-    GlobalLetVectorElemType,
-    GlobalLetMatrixElemType,
-    GlobalLetValue,
+    GlobalConstType,
+    GlobalConstArrayElemType,
+    GlobalConstArraySizeValue,
+    GlobalConstVectorElemType,
+    GlobalConstMatrixElemType,
+    GlobalConstValue,
     AliasType,
     StructMemberType,
     CallFunction,
@@ -146,11 +146,11 @@
     SymbolUseKind::GlobalVarMatrixElemType,
     SymbolUseKind::GlobalVarSampledTexElemType,
     SymbolUseKind::GlobalVarMultisampledTexElemType,
-    SymbolUseKind::GlobalLetType,
-    SymbolUseKind::GlobalLetArrayElemType,
-    SymbolUseKind::GlobalLetArraySizeValue,
-    SymbolUseKind::GlobalLetVectorElemType,
-    SymbolUseKind::GlobalLetMatrixElemType,
+    SymbolUseKind::GlobalConstType,
+    SymbolUseKind::GlobalConstArrayElemType,
+    SymbolUseKind::GlobalConstArraySizeValue,
+    SymbolUseKind::GlobalConstVectorElemType,
+    SymbolUseKind::GlobalConstMatrixElemType,
     SymbolUseKind::AliasType,
     SymbolUseKind::StructMemberType,
     SymbolUseKind::ParameterType,
@@ -165,7 +165,7 @@
 };
 
 static constexpr SymbolUseKind kValueUseKinds[] = {
-    SymbolUseKind::GlobalVarValue,      SymbolUseKind::GlobalLetValue,
+    SymbolUseKind::GlobalVarValue,      SymbolUseKind::GlobalConstValue,
     SymbolUseKind::LocalVarValue,       SymbolUseKind::LocalLetValue,
     SymbolUseKind::NestedLocalVarValue, SymbolUseKind::NestedLocalLetValue,
     SymbolUseKind::WorkgroupSizeValue,
@@ -182,7 +182,7 @@
         case SymbolDeclKind::GlobalVar:
             return out << "global var";
         case SymbolDeclKind::GlobalConst:
-            return out << "global let";
+            return out << "global const";
         case SymbolDeclKind::Alias:
             return out << "alias";
         case SymbolDeclKind::Struct:
@@ -223,18 +223,18 @@
             return out << "global var sampled_texture element type";
         case SymbolUseKind::GlobalVarMultisampledTexElemType:
             return out << "global var multisampled_texture element type";
-        case SymbolUseKind::GlobalLetType:
-            return out << "global let type";
-        case SymbolUseKind::GlobalLetValue:
-            return out << "global let value";
-        case SymbolUseKind::GlobalLetArrayElemType:
-            return out << "global let array element type";
-        case SymbolUseKind::GlobalLetArraySizeValue:
-            return out << "global let array size value";
-        case SymbolUseKind::GlobalLetVectorElemType:
-            return out << "global let vector element type";
-        case SymbolUseKind::GlobalLetMatrixElemType:
-            return out << "global let matrix element type";
+        case SymbolUseKind::GlobalConstType:
+            return out << "global const type";
+        case SymbolUseKind::GlobalConstValue:
+            return out << "global const value";
+        case SymbolUseKind::GlobalConstArrayElemType:
+            return out << "global const array element type";
+        case SymbolUseKind::GlobalConstArraySizeValue:
+            return out << "global const array size value";
+        case SymbolUseKind::GlobalConstVectorElemType:
+            return out << "global const vector element type";
+        case SymbolUseKind::GlobalConstMatrixElemType:
+            return out << "global const matrix element type";
         case SymbolUseKind::AliasType:
             return out << "alias type";
         case SymbolUseKind::StructMemberType:
@@ -282,10 +282,10 @@
         case SymbolUseKind::GlobalVarMatrixElemType:
         case SymbolUseKind::GlobalVarSampledTexElemType:
         case SymbolUseKind::GlobalVarMultisampledTexElemType:
-        case SymbolUseKind::GlobalLetType:
-        case SymbolUseKind::GlobalLetArrayElemType:
-        case SymbolUseKind::GlobalLetVectorElemType:
-        case SymbolUseKind::GlobalLetMatrixElemType:
+        case SymbolUseKind::GlobalConstType:
+        case SymbolUseKind::GlobalConstArrayElemType:
+        case SymbolUseKind::GlobalConstVectorElemType:
+        case SymbolUseKind::GlobalConstMatrixElemType:
         case SymbolUseKind::AliasType:
         case SymbolUseKind::StructMemberType:
         case SymbolUseKind::ParameterType:
@@ -299,8 +299,8 @@
             return "type";
         case SymbolUseKind::GlobalVarValue:
         case SymbolUseKind::GlobalVarArraySizeValue:
-        case SymbolUseKind::GlobalLetValue:
-        case SymbolUseKind::GlobalLetArraySizeValue:
+        case SymbolUseKind::GlobalConstValue:
+        case SymbolUseKind::GlobalConstArraySizeValue:
         case SymbolUseKind::LocalVarValue:
         case SymbolUseKind::LocalVarArraySizeValue:
         case SymbolUseKind::LocalLetValue:
@@ -349,12 +349,12 @@
         case SymbolUseKind::GlobalVarMatrixElemType:
         case SymbolUseKind::GlobalVarSampledTexElemType:
         case SymbolUseKind::GlobalVarMultisampledTexElemType:
-        case SymbolUseKind::GlobalLetType:
-        case SymbolUseKind::GlobalLetValue:
-        case SymbolUseKind::GlobalLetArrayElemType:
-        case SymbolUseKind::GlobalLetArraySizeValue:
-        case SymbolUseKind::GlobalLetVectorElemType:
-        case SymbolUseKind::GlobalLetMatrixElemType:
+        case SymbolUseKind::GlobalConstType:
+        case SymbolUseKind::GlobalConstValue:
+        case SymbolUseKind::GlobalConstArrayElemType:
+        case SymbolUseKind::GlobalConstArraySizeValue:
+        case SymbolUseKind::GlobalConstVectorElemType:
+        case SymbolUseKind::GlobalConstMatrixElemType:
         case SymbolUseKind::AliasType:
         case SymbolUseKind::StructMemberType:
         case SymbolUseKind::WorkgroupSizeValue:
@@ -425,7 +425,7 @@
     auto& b = *builder;
     switch (kind) {
         case SymbolDeclKind::GlobalVar:
-            return b.Global(source, symbol, b.ty.i32(), ast::StorageClass::kPrivate);
+            return b.GlobalVar(source, symbol, b.ty.i32(), ast::StorageClass::kPrivate);
         case SymbolDeclKind::GlobalConst:
             return b.GlobalConst(source, symbol, b.ty.i32(), b.Expr(1_i));
         case SymbolDeclKind::Alias:
@@ -468,70 +468,70 @@
     switch (kind) {
         case SymbolUseKind::GlobalVarType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Global(b.Sym(), node, ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), node, ast::StorageClass::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarArrayElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Global(b.Sym(), b.ty.array(node, 4_i), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.array(node, 4_i), ast::StorageClass::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarArraySizeValue: {
             auto* node = b.Expr(source, symbol);
-            b.Global(b.Sym(), b.ty.array(b.ty.i32(), node), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.array(b.ty.i32(), node), ast::StorageClass::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarVectorElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Global(b.Sym(), b.ty.vec3(node), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.vec3(node), ast::StorageClass::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarMatrixElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Global(b.Sym(), b.ty.mat3x4(node), ast::StorageClass::kPrivate);
+            b.GlobalVar(b.Sym(), b.ty.mat3x4(node), ast::StorageClass::kPrivate);
             return node;
         }
         case SymbolUseKind::GlobalVarSampledTexElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Global(b.Sym(), b.ty.sampled_texture(ast::TextureDimension::k2d, node));
+            b.GlobalVar(b.Sym(), b.ty.sampled_texture(ast::TextureDimension::k2d, node));
             return node;
         }
         case SymbolUseKind::GlobalVarMultisampledTexElemType: {
             auto* node = b.ty.type_name(source, symbol);
-            b.Global(b.Sym(), b.ty.multisampled_texture(ast::TextureDimension::k2d, node));
+            b.GlobalVar(b.Sym(), b.ty.multisampled_texture(ast::TextureDimension::k2d, node));
             return node;
         }
         case SymbolUseKind::GlobalVarValue: {
             auto* node = b.Expr(source, symbol);
-            b.Global(b.Sym(), b.ty.i32(), ast::StorageClass::kPrivate, node);
+            b.GlobalVar(b.Sym(), b.ty.i32(), ast::StorageClass::kPrivate, node);
             return node;
         }
-        case SymbolUseKind::GlobalLetType: {
+        case SymbolUseKind::GlobalConstType: {
             auto* node = b.ty.type_name(source, symbol);
             b.GlobalConst(b.Sym(), node, b.Expr(1_i));
             return node;
         }
-        case SymbolUseKind::GlobalLetArrayElemType: {
+        case SymbolUseKind::GlobalConstArrayElemType: {
             auto* node = b.ty.type_name(source, symbol);
             b.GlobalConst(b.Sym(), b.ty.array(node, 4_i), b.Expr(1_i));
             return node;
         }
-        case SymbolUseKind::GlobalLetArraySizeValue: {
+        case SymbolUseKind::GlobalConstArraySizeValue: {
             auto* node = b.Expr(source, symbol);
             b.GlobalConst(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1_i));
             return node;
         }
-        case SymbolUseKind::GlobalLetVectorElemType: {
+        case SymbolUseKind::GlobalConstVectorElemType: {
             auto* node = b.ty.type_name(source, symbol);
             b.GlobalConst(b.Sym(), b.ty.vec3(node), b.Expr(1_i));
             return node;
         }
-        case SymbolUseKind::GlobalLetMatrixElemType: {
+        case SymbolUseKind::GlobalConstMatrixElemType: {
             auto* node = b.ty.type_name(source, symbol);
             b.GlobalConst(b.Sym(), b.ty.mat3x4(node), b.Expr(1_i));
             return node;
         }
-        case SymbolUseKind::GlobalLetValue: {
+        case SymbolUseKind::GlobalConstValue: {
             auto* node = b.Expr(source, symbol);
             b.GlobalConst(b.Sym(), b.ty.i32(), node);
             return node;
@@ -722,7 +722,7 @@
              Block(Assign(Expr(Source{{12, 34}}, "G"), 3.14_f)),
          });
 
-    Global(Source{{56, 78}}, "G", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar(Source{{56, 78}}, "G", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
 
     Build();
 }
@@ -772,7 +772,7 @@
 
 TEST_F(ResolverDependencyGraphDeclSelfUse, GlobalVar) {
     const Symbol symbol = Sym("SYMBOL");
-    Global(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123_i));
+    GlobalVar(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123_i));
     Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL'
 12:34 note: var 'SYMBOL' references var 'SYMBOL' here)");
 }
@@ -781,7 +781,7 @@
     const Symbol symbol = Sym("SYMBOL");
     GlobalConst(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123_i));
     Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL'
-12:34 note: let 'SYMBOL' references let 'SYMBOL' here)");
+12:34 note: const 'SYMBOL' references const 'SYMBOL' here)");
 }
 
 TEST_F(ResolverDependencyGraphDeclSelfUse, LocalVar) {
@@ -899,7 +899,7 @@
 TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalVar_Direct) {
     // var<private> V : i32 = V;
 
-    Global(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
+    GlobalVar(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -907,7 +907,7 @@
 56:78 note: var 'V' references var 'V' here)");
 }
 
-TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalLet_Direct) {
+TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalConst_Direct) {
     // let V : i32 = V;
 
     GlobalConst(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
@@ -915,7 +915,7 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: cyclic dependency found: 'V' -> 'V'
-56:78 note: let 'V' references let 'V' here)");
+56:78 note: const 'V' references const 'V' here)");
 }
 
 TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalVar_Indirect) {
@@ -923,9 +923,9 @@
     // 2: var<private> X : i32 = Y;
     // 3: var<private> Z : i32 = X;
 
-    Global(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
-    Global(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
-    Global(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X"));
+    GlobalVar(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
+    GlobalVar(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
+    GlobalVar(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X"));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -935,10 +935,10 @@
 2:10 note: var 'X' references var 'Y' here)");
 }
 
-TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalLet_Indirect) {
-    // 1: let Y : i32 = Z;
-    // 2: let X : i32 = Y;
-    // 3: let Z : i32 = X;
+TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalConst_Indirect) {
+    // 1: const Y : i32 = Z;
+    // 2: const X : i32 = Y;
+    // 3: const Z : i32 = X;
 
     GlobalConst(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
     GlobalConst(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
@@ -947,9 +947,9 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
-1:10 note: let 'Y' references let 'Z' here
-3:10 note: let 'Z' references let 'X' here
-2:10 note: let 'X' references let 'Y' here)");
+1:10 note: const 'Y' references const 'Z' here
+3:10 note: const 'Z' references const 'X' here
+2:10 note: const 'X' references const 'Y' here)");
 }
 
 TEST_F(ResolverDependencyGraphCyclicRefTest, Mixed_RecursiveDependencies) {
@@ -958,13 +958,13 @@
     // 3: struct S { a : A };
     // 4: var Z = L;
     // 5: type R = A;
-    // 6: let L : S = Z;
+    // 6: const L : S = Z;
 
     Func(Source{{1, 1}}, "F", {}, ty.type_name(Source{{1, 5}}, "R"),
          {Return(Expr(Source{{1, 10}}, "Z"))});
     Alias(Source{{2, 1}}, "A", ty.type_name(Source{{2, 10}}, "S"));
     Structure(Source{{3, 1}}, "S", {Member("a", ty.type_name(Source{{3, 10}}, "A"))});
-    Global(Source{{4, 1}}, "Z", nullptr, Expr(Source{{4, 10}}, "L"));
+    GlobalVar(Source{{4, 1}}, "Z", nullptr, Expr(Source{{4, 10}}, "L"));
     Alias(Source{{5, 1}}, "R", ty.type_name(Source{{5, 10}}, "A"));
     GlobalConst(Source{{6, 1}}, "L", ty.type_name(Source{{5, 5}}, "S"), Expr(Source{{5, 10}}, "Z"));
 
@@ -974,8 +974,8 @@
 2:10 note: alias 'A' references struct 'S' here
 3:10 note: struct 'S' references alias 'A' here
 4:1 error: cyclic dependency found: 'Z' -> 'L' -> 'Z'
-4:10 note: var 'Z' references let 'L' here
-5:10 note: let 'L' references var 'Z' here)");
+4:10 note: var 'Z' references const 'L' here
+5:10 note: const 'L' references var 'Z' here)");
 }
 
 }  // namespace recursive_tests
@@ -1086,9 +1086,9 @@
     // Although all enable directives in a valid WGSL program must go before any other global
     // declaration, a transform may produce such a AST tree that has some declarations before enable
     // nodes. DependencyGraph should deal with these cases.
-    auto* var_1 = Global("SYMBOL1", ty.i32(), nullptr);
+    auto* var_1 = GlobalVar("SYMBOL1", ty.i32(), nullptr);
     auto* enable_1 = Enable(ast::Extension::kF16);
-    auto* var_2 = Global("SYMBOL2", ty.f32(), nullptr);
+    auto* var_2 = GlobalVar("SYMBOL2", ty.f32(), nullptr);
     auto* enable_2 = Enable(ast::Extension::kF16);
 
     EXPECT_THAT(AST().GlobalDeclarations(), ElementsAre(var_1, enable_1, var_2, enable_2));
@@ -1201,7 +1201,7 @@
     const auto type_sym = Sym("TYPE");
     const auto func_sym = Sym("FUNC");
 
-    const auto* value_decl = Global(value_sym, ty.i32(), ast::StorageClass::kPrivate);
+    const auto* value_decl = GlobalVar(value_sym, ty.i32(), ast::StorageClass::kPrivate);
     const auto* type_decl = Alias(type_sym, ty.i32());
     const auto* func_decl = Func(func_sym, {}, ty.void_(), {});
 
@@ -1224,7 +1224,7 @@
 
     Alias(Sym(), T);
     Structure(Sym(), {Member(Sym(), T)});
-    Global(Sym(), T, V);
+    GlobalVar(Sym(), T, V);
     GlobalConst(Sym(), T, V);
     Func(Sym(),              //
          {Param(Sym(), T)},  //
@@ -1261,23 +1261,23 @@
              Discard(),                                 //
          });                                            //
     // Exercise type traversal
-    Global(Sym(), ty.atomic(T));
-    Global(Sym(), ty.bool_());
-    Global(Sym(), ty.i32());
-    Global(Sym(), ty.u32());
-    Global(Sym(), ty.f32());
-    Global(Sym(), ty.array(T, V, 4));
-    Global(Sym(), ty.vec3(T));
-    Global(Sym(), ty.mat3x2(T));
-    Global(Sym(), ty.pointer(T, ast::StorageClass::kPrivate));
-    Global(Sym(), ty.sampled_texture(ast::TextureDimension::k2d, T));
-    Global(Sym(), ty.depth_texture(ast::TextureDimension::k2d));
-    Global(Sym(), ty.depth_multisampled_texture(ast::TextureDimension::k2d));
-    Global(Sym(), ty.external_texture());
-    Global(Sym(), ty.multisampled_texture(ast::TextureDimension::k2d, T));
-    Global(Sym(), ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
-                                     ast::Access::kRead));  //
-    Global(Sym(), ty.sampler(ast::SamplerKind::kSampler));
+    GlobalVar(Sym(), ty.atomic(T));
+    GlobalVar(Sym(), ty.bool_());
+    GlobalVar(Sym(), ty.i32());
+    GlobalVar(Sym(), ty.u32());
+    GlobalVar(Sym(), ty.f32());
+    GlobalVar(Sym(), ty.array(T, V, 4));
+    GlobalVar(Sym(), ty.vec3(T));
+    GlobalVar(Sym(), ty.mat3x2(T));
+    GlobalVar(Sym(), ty.pointer(T, ast::StorageClass::kPrivate));
+    GlobalVar(Sym(), ty.sampled_texture(ast::TextureDimension::k2d, T));
+    GlobalVar(Sym(), ty.depth_texture(ast::TextureDimension::k2d));
+    GlobalVar(Sym(), ty.depth_multisampled_texture(ast::TextureDimension::k2d));
+    GlobalVar(Sym(), ty.external_texture());
+    GlobalVar(Sym(), ty.multisampled_texture(ast::TextureDimension::k2d, T));
+    GlobalVar(Sym(), ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
+                                        ast::Access::kRead));  //
+    GlobalVar(Sym(), ty.sampler(ast::SamplerKind::kSampler));
     Func(Sym(), {}, ty.void_(), {});
 #undef V
 #undef T
@@ -1291,8 +1291,8 @@
 }
 
 TEST_F(ResolverDependencyGraphTraversalTest, InferredType) {
-    // Check that the nullptr of the var / let type doesn't make things explode
-    Global("a", nullptr, Expr(1_i));
+    // Check that the nullptr of the var / const / let type doesn't make things explode
+    GlobalVar("a", nullptr, Expr(1_i));
     GlobalConst("b", nullptr, Expr(1_i));
     WrapInFunction(Var("c", nullptr, Expr(1_i)),  //
                    Let("d", nullptr, Expr(1_i)));
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index 525195f..7039fa4 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -39,7 +39,7 @@
 TEST_F(ResolverFunctionValidationTest, ParameterMayShadowGlobal) {
     // var<private> common_name : f32;
     // fn func(common_name : f32) { }
-    Global("common_name", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("common_name", ty.f32(), ast::StorageClass::kPrivate);
     Func("func", {Param("common_name", ty.f32())}, ty.void_(), {});
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -241,8 +241,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: return statement type must match its function return "
-              "type, returned 'abstract-int', expected 'void'");
+              "12:34 error: return statement type must match its function return type, returned "
+              "'abstract-int', expected 'void'");
 }
 
 TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsAFloat) {
@@ -251,8 +251,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: return statement type must match its function return "
-              "type, returned 'abstract-float', expected 'void'");
+              "12:34 error: return statement type must match its function return type, returned "
+              "'abstract-float', expected 'void'");
 }
 
 TEST_F(ResolverFunctionValidationTest, VoidFunctionReturnsI32) {
@@ -261,8 +261,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: return statement type must match its function return "
-              "type, returned 'i32', expected 'void'");
+              "12:34 error: return statement type must match its function return type, returned "
+              "'i32', expected 'void'");
 }
 
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_void_fail) {
@@ -287,8 +287,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: return statement type must match its function return "
-              "type, returned 'void', expected 'f32'");
+              "12:34 error: return statement type must match its function return type, returned "
+              "'void', expected 'f32'");
 }
 
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_pass) {
@@ -310,8 +310,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: return statement type must match its function return "
-              "type, returned 'i32', expected 'f32'");
+              "12:34 error: return statement type must match its function return type, returned "
+              "'i32', expected 'f32'");
 }
 
 TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32Alias_pass) {
@@ -337,8 +337,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: return statement type must match its function return "
-              "type, returned 'u32', expected 'f32'");
+              "12:34 error: return statement type must match its function return type, returned "
+              "'u32', expected 'f32'");
 }
 
 TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
@@ -354,10 +354,19 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-
               R"(12:34 error: entry point functions cannot be the target of a function call)");
 }
 
+TEST_F(ResolverFunctionValidationTest, CannotCallFunctionAtModuleScope) {
+    // fn F() -> i32 { return 1; }
+    // var x = F();
+    Func("F", {}, ty.i32(), {Return(1_i)});
+    GlobalVar("x", nullptr, Call(Source{{12, 34}}, "F"), ast::StorageClass::kPrivate);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: functions cannot be called at module-scope)");
+}
+
 TEST_F(ResolverFunctionValidationTest, PipelineStage_MustBeUnique_Fail) {
     // @fragment
     // @vertex
@@ -424,13 +433,12 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: cannot assign to function parameter\nnote: 'arg' is "
-              "declared here:");
+              "12:34 error: cannot assign to function parameter\nnote: 'arg' is declared here:");
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_ConstU32) {
-    // let x = 4u;
-    // let x = 8u;
+    // const x = 4u;
+    // const x = 8u;
     // @compute @workgroup_size(x, y, 16u)
     // fn main() {}
     auto* x = GlobalConst("x", ty.u32(), Expr(4_u));
@@ -517,7 +525,7 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_TypeMismatch) {
-    // let x = 64u;
+    // const x = 64u;
     // @compute @workgroup_size(1i, x)
     // fn main() {}
     GlobalConst("x", ty.u32(), Expr(64_u));
@@ -530,8 +538,8 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_TypeMismatch2) {
-    // let x = 64u;
-    // let y = 32i;
+    // const x = 64u;
+    // const y = 32i;
     // @compute @workgroup_size(x, y)
     // fn main() {}
     GlobalConst("x", ty.u32(), Expr(64_u));
@@ -543,9 +551,10 @@
     EXPECT_EQ(r()->error(),
               "12:34 error: workgroup_size arguments must be of the same type, either i32 or u32");
 }
+
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Mismatch_ConstU32) {
-    // let x = 4u;
-    // let x = 8u;
+    // const x = 4u;
+    // const x = 8u;
     // @compute @workgroup_size(x, y, 16i)
     // fn main() {}
     GlobalConst("x", ty.u32(), Expr(4_u));
@@ -567,8 +576,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: workgroup_size argument must be either literal or "
-              "module-scope constant of type i32 or u32");
+              "12:34 error: workgroup_size argument must be either a literal, constant, or "
+              "overridable of type abstract-integer, i32 or u32");
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_Negative) {
@@ -594,7 +603,7 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_BadType) {
-    // let x = 64.0;
+    // const x = 64.0;
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.f32(), Expr(64_f));
@@ -603,12 +612,12 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: workgroup_size argument must be either literal or "
-              "module-scope constant of type i32 or u32");
+              "12:34 error: workgroup_size argument must be either a literal, constant, or "
+              "overridable of type abstract-integer, i32 or u32");
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_Negative) {
-    // let x = -2i;
+    // const x = -2i;
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.i32(), Expr(-2_i));
@@ -620,7 +629,7 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_Zero) {
-    // let x = 0i;
+    // const x = 0i;
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.i32(), Expr(0_i));
@@ -632,7 +641,7 @@
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_NestedZeroValueConstructor) {
-    // let x = i32(i32(i32()));
+    // const x = i32(i32(i32()));
     // @compute @workgroup_size(x)
     // fn main() {}
     GlobalConst("x", ty.i32(), Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32()))));
@@ -647,14 +656,14 @@
     // var<private> x = 64i;
     // @compute @workgroup_size(x)
     // fn main() {}
-    Global("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64_i));
+    GlobalVar("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64_i));
     Func("main", {}, ty.void_(), {},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: workgroup_size argument must be either literal or "
-              "module-scope constant of type i32 or u32");
+              "12:34 error: workgroup_size argument must be either a literal, constant, or "
+              "overridable of type abstract-integer, i32 or u32");
 }
 
 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_InvalidExpr) {
@@ -666,8 +675,8 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: workgroup_size argument must be either a literal or "
-              "a module-scope constant");
+              "12:34 error: workgroup_size argument must be either a literal, constant, or "
+              "overridable of type abstract-integer, i32 or u32");
 }
 
 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_NonPlain) {
@@ -754,7 +763,7 @@
 TEST_F(ResolverFunctionValidationTest, ParameterVectorNoType) {
     // fn f(p : vec3) {}
 
-    Func(Source{{12, 34}}, "f", {Param("p", create<ast::Vector>(Source{{12, 34}}, nullptr, 3))},
+    Func(Source{{12, 34}}, "f", {Param("p", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u))},
          ty.void_(), {});
 
     EXPECT_FALSE(r()->Resolve());
@@ -764,8 +773,8 @@
 TEST_F(ResolverFunctionValidationTest, ParameterMatrixNoType) {
     // fn f(p : vec3) {}
 
-    Func(Source{{12, 34}}, "f", {Param("p", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3))},
-         ty.void_(), {});
+    Func(Source{{12, 34}}, "f",
+         {Param("p", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u))}, ty.void_(), {});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index 01fbfb0..d50057c 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -26,11 +26,11 @@
 TEST_F(ResolverHostShareableValidationTest, BoolMember) {
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())});
 
-    Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
@@ -38,17 +38,17 @@
         r()->error(),
         R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
 12:34 note: while analysing structure member S.x
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
 
-    Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
@@ -56,18 +56,18 @@
         r()->error(),
         R"(56:78 error: Type 'vec3<bool>' cannot be used in storage class 'storage' as it is non-host-shareable
 12:34 note: while analysing structure member S.x
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, Aliases) {
     auto* a1 = Alias("a1", ty.bool_());
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.Of(a1))});
     auto* a2 = Alias("a2", ty.Of(s));
-    Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
@@ -75,7 +75,7 @@
         r()->error(),
         R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
 12:34 note: while analysing structure member S.x
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
@@ -85,11 +85,11 @@
 
     auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
-    Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
@@ -100,19 +100,23 @@
 3:4 note: while analysing structure member I2.y
 5:6 note: while analysing structure member I3.z
 7:8 note: while analysing structure member S.m
-9:10 note: while instantiating variable g)");
+9:10 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverHostShareableValidationTest, NoError) {
+    Enable(ast::Extension::kF16);
+
     auto* i1 = Structure("I1", {
-                                   Member(Source{{1, 1}}, "x1", ty.f32()),
-                                   Member(Source{{2, 1}}, "y1", ty.vec3<f32>()),
-                                   Member(Source{{3, 1}}, "z1", ty.array<i32, 4>()),
+                                   Member(Source{{1, 1}}, "w1", ty.f16()),
+                                   Member(Source{{2, 1}}, "x1", ty.f32()),
+                                   Member(Source{{3, 1}}, "y1", ty.vec3<f32>()),
+                                   Member(Source{{4, 1}}, "z1", ty.array<i32, 4>()),
                                });
     auto* a1 = Alias("a1", ty.Of(i1));
     auto* i2 = Structure("I2", {
-                                   Member(Source{{4, 1}}, "x2", ty.mat2x2<f32>()),
-                                   Member(Source{{5, 1}}, "y2", ty.Of(i1)),
+                                   Member(Source{{5, 1}}, "x2", ty.mat2x2<f32>()),
+                                   Member(Source{{6, 1}}, "w2", ty.mat3x4<f16>()),
+                                   Member(Source{{7, 1}}, "z2", ty.Of(i1)),
                                });
     auto* a2 = Alias("a2", ty.Of(i2));
     auto* i3 = Structure("I3", {
@@ -123,11 +127,11 @@
 
     auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
 
-    Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
     WrapInFunction();
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/increment_decrement_validation_test.cc b/src/tint/resolver/increment_decrement_validation_test.cc
index b8e3aa1..5061e49 100644
--- a/src/tint/resolver/increment_decrement_validation_test.cc
+++ b/src/tint/resolver/increment_decrement_validation_test.cc
@@ -127,7 +127,7 @@
 TEST_F(ResolverIncrementDecrementValidationTest, Atomic) {
     // var<workgroup> a : atomic<i32>;
     // a++;
-    Global(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
     WrapInFunction(Increment(Expr(Source{{56, 78}}, "a")));
 
     EXPECT_FALSE(r()->Resolve());
@@ -187,8 +187,8 @@
     // {
     //   a++;
     // }
-    Global(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+              GroupAndBinding(0, 0));
     WrapInFunction(Increment(Source{{56, 78}}, "a"));
 
     EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/inferred_type_test.cc b/src/tint/resolver/inferred_type_test.cc
index 4d01f7e..a183b20 100644
--- a/src/tint/resolver/inferred_type_test.cc
+++ b/src/tint/resolver/inferred_type_test.cc
@@ -75,30 +75,32 @@
 
 using ResolverInferredTypeParamTest = ResolverTestWithParam<Params>;
 
-TEST_P(ResolverInferredTypeParamTest, GlobalLet_Pass) {
+TEST_P(ResolverInferredTypeParamTest, GlobalConst_Pass) {
     auto& params = GetParam();
 
     auto* expected_type = params.create_expected_type(*this);
 
-    // let a = <type constructor>;
+    // const a = <type constructor>;
     auto* ctor_expr = params.create_value(*this, 0);
-    auto* var = GlobalConst("a", nullptr, ctor_expr);
+    auto* a = GlobalConst("a", nullptr, ctor_expr);
     WrapInFunction();
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
-    EXPECT_EQ(TypeOf(var), expected_type);
+    EXPECT_EQ(TypeOf(a), expected_type);
 }
 
-TEST_P(ResolverInferredTypeParamTest, GlobalVar_Fail) {
+TEST_P(ResolverInferredTypeParamTest, GlobalVar_Pass) {
     auto& params = GetParam();
 
+    auto* expected_type = params.create_expected_type(*this);
+
     // var a = <type constructor>;
     auto* ctor_expr = params.create_value(*this, 0);
-    Global(Source{{12, 34}}, "a", nullptr, ast::StorageClass::kPrivate, ctor_expr);
+    auto* var = GlobalVar("a", nullptr, ast::StorageClass::kPrivate, ctor_expr);
     WrapInFunction();
 
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: module-scope 'var' declaration must specify a type");
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
 }
 
 TEST_P(ResolverInferredTypeParamTest, LocalLet_Pass) {
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index eb8f30e..d746a6c 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -19,6 +19,7 @@
 #include <unordered_map>
 #include <utility>
 
+#include "src/tint/ast/binary_expression.h"
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/abstract_float.h"
 #include "src/tint/sem/abstract_int.h"
@@ -341,6 +342,14 @@
     return state.builder.create<sem::Bool>();
 }
 
+const sem::F16* build_f16(MatchState& state) {
+    return state.builder.create<sem::F16>();
+}
+
+bool match_f16(const sem::Type* ty) {
+    return ty->IsAnyOf<Any, sem::F16, sem::AbstractNumeric>();
+}
+
 const sem::F32* build_f32(MatchState& state) {
     return state.builder.create<sem::F32>();
 }
@@ -1291,8 +1300,8 @@
     // Conversion.
     return utils::GetOrCreate(converters, match, [&]() {
         auto param = builder.create<sem::Parameter>(
-            nullptr, 0, match.parameters[0].type, ast::StorageClass::kNone, ast::Access::kUndefined,
-            match.parameters[0].usage);
+            nullptr, 0u, match.parameters[0].type, ast::StorageClass::kNone,
+            ast::Access::kUndefined, match.parameters[0].usage);
         return builder.create<sem::TypeConversion>(match.return_type, param);
     });
 }
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index 058b253..d10f7bb 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -19,6 +19,8 @@
 #include <string>
 #include <vector>
 
+#include "src/tint/ast/binary_expression.h"
+#include "src/tint/ast/unary_op.h"
 #include "src/tint/resolver/const_eval.h"
 #include "src/tint/resolver/ctor_conv_intrinsic.h"
 #include "src/tint/sem/builtin.h"
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index 92a11f2..7869059 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -190,8 +190,35 @@
   return "f32";
 }
 
+/// TypeMatcher for 'type f16'
+/// @see src/tint/intrinsics.def:79:21
+class F16 : public TypeMatcher {
+ public:
+  /// Checks whether the given type matches the matcher rules.
+  /// Match may define and refine the template types and numbers in state.
+  /// @param state the MatchState
+  /// @param type the type to match
+  /// @returns the canonicalized type on match, otherwise nullptr
+  const sem::Type* Match(MatchState& state,
+                         const sem::Type* type) const override;
+  /// @param state the MatchState
+  /// @return a string representation of the matcher.
+  std::string String(MatchState* state) const override;
+};
+
+const sem::Type* F16::Match(MatchState& state, const sem::Type* ty) const {
+  if (!match_f16(ty)) {
+    return nullptr;
+  }
+  return build_f16(state);
+}
+
+std::string F16::String(MatchState*) const {
+  return "f16";
+}
+
 /// TypeMatcher for 'type vec2'
-/// @see src/tint/intrinsics.def:79:6
+/// @see src/tint/intrinsics.def:80:6
 class Vec2 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -224,7 +251,7 @@
 }
 
 /// TypeMatcher for 'type vec3'
-/// @see src/tint/intrinsics.def:80:6
+/// @see src/tint/intrinsics.def:81:6
 class Vec3 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -257,7 +284,7 @@
 }
 
 /// TypeMatcher for 'type vec4'
-/// @see src/tint/intrinsics.def:81:6
+/// @see src/tint/intrinsics.def:82:6
 class Vec4 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -290,7 +317,7 @@
 }
 
 /// TypeMatcher for 'type mat2x2'
-/// @see src/tint/intrinsics.def:82:6
+/// @see src/tint/intrinsics.def:83:6
 class Mat2X2 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -323,7 +350,7 @@
 }
 
 /// TypeMatcher for 'type mat2x3'
-/// @see src/tint/intrinsics.def:83:6
+/// @see src/tint/intrinsics.def:84:6
 class Mat2X3 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -356,7 +383,7 @@
 }
 
 /// TypeMatcher for 'type mat2x4'
-/// @see src/tint/intrinsics.def:84:6
+/// @see src/tint/intrinsics.def:85:6
 class Mat2X4 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -389,7 +416,7 @@
 }
 
 /// TypeMatcher for 'type mat3x2'
-/// @see src/tint/intrinsics.def:85:6
+/// @see src/tint/intrinsics.def:86:6
 class Mat3X2 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -422,7 +449,7 @@
 }
 
 /// TypeMatcher for 'type mat3x3'
-/// @see src/tint/intrinsics.def:86:6
+/// @see src/tint/intrinsics.def:87:6
 class Mat3X3 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -455,7 +482,7 @@
 }
 
 /// TypeMatcher for 'type mat3x4'
-/// @see src/tint/intrinsics.def:87:6
+/// @see src/tint/intrinsics.def:88:6
 class Mat3X4 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -488,7 +515,7 @@
 }
 
 /// TypeMatcher for 'type mat4x2'
-/// @see src/tint/intrinsics.def:88:6
+/// @see src/tint/intrinsics.def:89:6
 class Mat4X2 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -521,7 +548,7 @@
 }
 
 /// TypeMatcher for 'type mat4x3'
-/// @see src/tint/intrinsics.def:89:6
+/// @see src/tint/intrinsics.def:90:6
 class Mat4X3 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -554,7 +581,7 @@
 }
 
 /// TypeMatcher for 'type mat4x4'
-/// @see src/tint/intrinsics.def:90:6
+/// @see src/tint/intrinsics.def:91:6
 class Mat4X4 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -587,7 +614,7 @@
 }
 
 /// TypeMatcher for 'type vec'
-/// @see src/tint/intrinsics.def:91:34
+/// @see src/tint/intrinsics.def:92:34
 class Vec : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -628,7 +655,7 @@
 }
 
 /// TypeMatcher for 'type mat'
-/// @see src/tint/intrinsics.def:92:34
+/// @see src/tint/intrinsics.def:93:34
 class Mat : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -675,7 +702,7 @@
 }
 
 /// TypeMatcher for 'type ptr'
-/// @see src/tint/intrinsics.def:93:6
+/// @see src/tint/intrinsics.def:94:6
 class Ptr : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -720,7 +747,7 @@
 }
 
 /// TypeMatcher for 'type atomic'
-/// @see src/tint/intrinsics.def:94:6
+/// @see src/tint/intrinsics.def:95:6
 class Atomic : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -753,7 +780,7 @@
 }
 
 /// TypeMatcher for 'type array'
-/// @see src/tint/intrinsics.def:95:6
+/// @see src/tint/intrinsics.def:96:6
 class Array : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -786,7 +813,7 @@
 }
 
 /// TypeMatcher for 'type sampler'
-/// @see src/tint/intrinsics.def:96:6
+/// @see src/tint/intrinsics.def:97:6
 class Sampler : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -813,7 +840,7 @@
 }
 
 /// TypeMatcher for 'type sampler_comparison'
-/// @see src/tint/intrinsics.def:97:6
+/// @see src/tint/intrinsics.def:98:6
 class SamplerComparison : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -840,7 +867,7 @@
 }
 
 /// TypeMatcher for 'type texture_1d'
-/// @see src/tint/intrinsics.def:98:6
+/// @see src/tint/intrinsics.def:99:6
 class Texture1D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -873,7 +900,7 @@
 }
 
 /// TypeMatcher for 'type texture_2d'
-/// @see src/tint/intrinsics.def:99:6
+/// @see src/tint/intrinsics.def:100:6
 class Texture2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -906,7 +933,7 @@
 }
 
 /// TypeMatcher for 'type texture_2d_array'
-/// @see src/tint/intrinsics.def:100:6
+/// @see src/tint/intrinsics.def:101:6
 class Texture2DArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -939,7 +966,7 @@
 }
 
 /// TypeMatcher for 'type texture_3d'
-/// @see src/tint/intrinsics.def:101:6
+/// @see src/tint/intrinsics.def:102:6
 class Texture3D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -972,7 +999,7 @@
 }
 
 /// TypeMatcher for 'type texture_cube'
-/// @see src/tint/intrinsics.def:102:6
+/// @see src/tint/intrinsics.def:103:6
 class TextureCube : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1005,7 +1032,7 @@
 }
 
 /// TypeMatcher for 'type texture_cube_array'
-/// @see src/tint/intrinsics.def:103:6
+/// @see src/tint/intrinsics.def:104:6
 class TextureCubeArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1038,7 +1065,7 @@
 }
 
 /// TypeMatcher for 'type texture_multisampled_2d'
-/// @see src/tint/intrinsics.def:104:6
+/// @see src/tint/intrinsics.def:105:6
 class TextureMultisampled2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1071,7 +1098,7 @@
 }
 
 /// TypeMatcher for 'type texture_depth_2d'
-/// @see src/tint/intrinsics.def:105:6
+/// @see src/tint/intrinsics.def:106:6
 class TextureDepth2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1098,7 +1125,7 @@
 }
 
 /// TypeMatcher for 'type texture_depth_2d_array'
-/// @see src/tint/intrinsics.def:106:6
+/// @see src/tint/intrinsics.def:107:6
 class TextureDepth2DArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1125,7 +1152,7 @@
 }
 
 /// TypeMatcher for 'type texture_depth_cube'
-/// @see src/tint/intrinsics.def:107:6
+/// @see src/tint/intrinsics.def:108:6
 class TextureDepthCube : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1152,7 +1179,7 @@
 }
 
 /// TypeMatcher for 'type texture_depth_cube_array'
-/// @see src/tint/intrinsics.def:108:6
+/// @see src/tint/intrinsics.def:109:6
 class TextureDepthCubeArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1179,7 +1206,7 @@
 }
 
 /// TypeMatcher for 'type texture_depth_multisampled_2d'
-/// @see src/tint/intrinsics.def:109:6
+/// @see src/tint/intrinsics.def:110:6
 class TextureDepthMultisampled2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1206,7 +1233,7 @@
 }
 
 /// TypeMatcher for 'type texture_storage_1d'
-/// @see src/tint/intrinsics.def:110:6
+/// @see src/tint/intrinsics.def:111:6
 class TextureStorage1D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1245,7 +1272,7 @@
 }
 
 /// TypeMatcher for 'type texture_storage_2d'
-/// @see src/tint/intrinsics.def:111:6
+/// @see src/tint/intrinsics.def:112:6
 class TextureStorage2D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1284,7 +1311,7 @@
 }
 
 /// TypeMatcher for 'type texture_storage_2d_array'
-/// @see src/tint/intrinsics.def:112:6
+/// @see src/tint/intrinsics.def:113:6
 class TextureStorage2DArray : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1323,7 +1350,7 @@
 }
 
 /// TypeMatcher for 'type texture_storage_3d'
-/// @see src/tint/intrinsics.def:113:6
+/// @see src/tint/intrinsics.def:114:6
 class TextureStorage3D : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1362,7 +1389,7 @@
 }
 
 /// TypeMatcher for 'type texture_external'
-/// @see src/tint/intrinsics.def:114:6
+/// @see src/tint/intrinsics.def:115:6
 class TextureExternal : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1389,7 +1416,7 @@
 }
 
 /// TypeMatcher for 'type __modf_result'
-/// @see src/tint/intrinsics.def:116:6
+/// @see src/tint/intrinsics.def:117:6
 class ModfResult : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1416,7 +1443,7 @@
 }
 
 /// TypeMatcher for 'type __modf_result_vec'
-/// @see src/tint/intrinsics.def:117:39
+/// @see src/tint/intrinsics.def:118:39
 class ModfResultVec : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1451,7 +1478,7 @@
 }
 
 /// TypeMatcher for 'type __frexp_result'
-/// @see src/tint/intrinsics.def:118:6
+/// @see src/tint/intrinsics.def:119:6
 class FrexpResult : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1478,7 +1505,7 @@
 }
 
 /// TypeMatcher for 'type __frexp_result_vec'
-/// @see src/tint/intrinsics.def:119:40
+/// @see src/tint/intrinsics.def:120:40
 class FrexpResultVec : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1513,7 +1540,7 @@
 }
 
 /// TypeMatcher for 'type __atomic_compare_exchange_result'
-/// @see src/tint/intrinsics.def:121:6
+/// @see src/tint/intrinsics.def:122:6
 class AtomicCompareExchangeResult : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules.
@@ -1545,8 +1572,43 @@
   return "__atomic_compare_exchange_result<" + T + ">";
 }
 
+/// TypeMatcher for 'match f32f16'
+/// @see src/tint/intrinsics.def:130:7
+class F32F16 : public TypeMatcher {
+ public:
+  /// Checks whether the given type matches the matcher rules, and returns the
+  /// expected, canonicalized type on success.
+  /// Match may define and refine the template types and numbers in state.
+  /// @param state the MatchState
+  /// @param type the type to match
+  /// @returns the canonicalized type on match, otherwise nullptr
+  const sem::Type* Match(MatchState& state,
+                         const sem::Type* type) const override;
+  /// @param state the MatchState
+  /// @return a string representation of the matcher.
+  std::string String(MatchState* state) const override;
+};
+
+const sem::Type* F32F16::Match(MatchState& state, const sem::Type* ty) const {
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
+  return nullptr;
+}
+
+std::string F32F16::String(MatchState*) const {
+  std::stringstream ss;
+  // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
+  // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
+  ss << F32().String(nullptr) << " or " << F16().String(nullptr);
+  return ss.str();
+}
+
 /// TypeMatcher for 'match fiu32'
-/// @see src/tint/intrinsics.def:129:7
+/// @see src/tint/intrinsics.def:131:7
 class Fiu32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1584,7 +1646,7 @@
 }
 
 /// TypeMatcher for 'match fi32'
-/// @see src/tint/intrinsics.def:130:7
+/// @see src/tint/intrinsics.def:132:7
 class Fi32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1619,7 +1681,7 @@
 }
 
 /// TypeMatcher for 'match iu32'
-/// @see src/tint/intrinsics.def:131:7
+/// @see src/tint/intrinsics.def:133:7
 class Iu32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1654,7 +1716,7 @@
 }
 
 /// TypeMatcher for 'match scalar'
-/// @see src/tint/intrinsics.def:132:7
+/// @see src/tint/intrinsics.def:134:7
 class Scalar : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1680,6 +1742,9 @@
   if (match_f32(ty)) {
     return build_f32(state);
   }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
   if (match_bool(ty)) {
     return build_bool(state);
   }
@@ -1690,12 +1755,12 @@
   std::stringstream ss;
   // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
   // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
-  ss << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
   return ss.str();
 }
 
 /// TypeMatcher for 'match abstract_or_scalar'
-/// @see src/tint/intrinsics.def:133:7
+/// @see src/tint/intrinsics.def:135:7
 class AbstractOrScalar : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1727,6 +1792,9 @@
   if (match_f32(ty)) {
     return build_f32(state);
   }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
   if (match_bool(ty)) {
     return build_bool(state);
   }
@@ -1737,12 +1805,12 @@
   std::stringstream ss;
   // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
   // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
-  ss << Ai().String(nullptr) << ", " << Af().String(nullptr) << ", " << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  ss << Ai().String(nullptr) << ", " << Af().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
   return ss.str();
 }
 
 /// TypeMatcher for 'match af_f32'
-/// @see src/tint/intrinsics.def:134:7
+/// @see src/tint/intrinsics.def:136:7
 class AfF32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1776,8 +1844,46 @@
   return ss.str();
 }
 
+/// TypeMatcher for 'match af_f32f16'
+/// @see src/tint/intrinsics.def:137:7
+class AfF32F16 : public TypeMatcher {
+ public:
+  /// Checks whether the given type matches the matcher rules, and returns the
+  /// expected, canonicalized type on success.
+  /// Match may define and refine the template types and numbers in state.
+  /// @param state the MatchState
+  /// @param type the type to match
+  /// @returns the canonicalized type on match, otherwise nullptr
+  const sem::Type* Match(MatchState& state,
+                         const sem::Type* type) const override;
+  /// @param state the MatchState
+  /// @return a string representation of the matcher.
+  std::string String(MatchState* state) const override;
+};
+
+const sem::Type* AfF32F16::Match(MatchState& state, const sem::Type* ty) const {
+  if (match_af(ty)) {
+    return build_af(state);
+  }
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
+  return nullptr;
+}
+
+std::string AfF32F16::String(MatchState*) const {
+  std::stringstream ss;
+  // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
+  // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
+  ss << Af().String(nullptr) << ", " << F32().String(nullptr) << " or " << F16().String(nullptr);
+  return ss.str();
+}
+
 /// TypeMatcher for 'match scalar_no_f32'
-/// @see src/tint/intrinsics.def:135:7
+/// @see src/tint/intrinsics.def:138:7
 class ScalarNoF32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1800,6 +1906,9 @@
   if (match_u32(ty)) {
     return build_u32(state);
   }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
   if (match_bool(ty)) {
     return build_bool(state);
   }
@@ -1810,12 +1919,53 @@
   std::stringstream ss;
   // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
   // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
-  ss << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  ss << I32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  return ss.str();
+}
+
+/// TypeMatcher for 'match scalar_no_f16'
+/// @see src/tint/intrinsics.def:139:7
+class ScalarNoF16 : public TypeMatcher {
+ public:
+  /// Checks whether the given type matches the matcher rules, and returns the
+  /// expected, canonicalized type on success.
+  /// Match may define and refine the template types and numbers in state.
+  /// @param state the MatchState
+  /// @param type the type to match
+  /// @returns the canonicalized type on match, otherwise nullptr
+  const sem::Type* Match(MatchState& state,
+                         const sem::Type* type) const override;
+  /// @param state the MatchState
+  /// @return a string representation of the matcher.
+  std::string String(MatchState* state) const override;
+};
+
+const sem::Type* ScalarNoF16::Match(MatchState& state, const sem::Type* ty) const {
+  if (match_i32(ty)) {
+    return build_i32(state);
+  }
+  if (match_u32(ty)) {
+    return build_u32(state);
+  }
+  if (match_f32(ty)) {
+    return build_f32(state);
+  }
+  if (match_bool(ty)) {
+    return build_bool(state);
+  }
+  return nullptr;
+}
+
+std::string ScalarNoF16::String(MatchState*) const {
+  std::stringstream ss;
+  // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
+  // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
+  ss << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
   return ss.str();
 }
 
 /// TypeMatcher for 'match scalar_no_i32'
-/// @see src/tint/intrinsics.def:136:7
+/// @see src/tint/intrinsics.def:140:7
 class ScalarNoI32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1838,6 +1988,9 @@
   if (match_f32(ty)) {
     return build_f32(state);
   }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
   if (match_bool(ty)) {
     return build_bool(state);
   }
@@ -1848,12 +2001,12 @@
   std::stringstream ss;
   // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
   // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
-  ss << F32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+  ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
   return ss.str();
 }
 
 /// TypeMatcher for 'match scalar_no_u32'
-/// @see src/tint/intrinsics.def:137:7
+/// @see src/tint/intrinsics.def:141:7
 class ScalarNoU32 : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1876,6 +2029,9 @@
   if (match_f32(ty)) {
     return build_f32(state);
   }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
   if (match_bool(ty)) {
     return build_bool(state);
   }
@@ -1886,12 +2042,12 @@
   std::stringstream ss;
   // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
   // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
-  ss << F32().String(nullptr) << ", " << I32().String(nullptr) << " or " << Bool().String(nullptr);
+  ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << Bool().String(nullptr);
   return ss.str();
 }
 
 /// TypeMatcher for 'match scalar_no_bool'
-/// @see src/tint/intrinsics.def:138:7
+/// @see src/tint/intrinsics.def:142:7
 class ScalarNoBool : public TypeMatcher {
  public:
   /// Checks whether the given type matches the matcher rules, and returns the
@@ -1917,6 +2073,9 @@
   if (match_f32(ty)) {
     return build_f32(state);
   }
+  if (match_f16(ty)) {
+    return build_f16(state);
+  }
   return nullptr;
 }
 
@@ -1924,12 +2083,12 @@
   std::stringstream ss;
   // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
   // template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
-  ss << F32().String(nullptr) << ", " << I32().String(nullptr) << " or " << U32().String(nullptr);
+  ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << U32().String(nullptr);
   return ss.str();
 }
 
 /// EnumMatcher for 'match f32_texel_format'
-/// @see src/tint/intrinsics.def:149:7
+/// @see src/tint/intrinsics.def:153:7
 class F32TexelFormat : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
@@ -1962,7 +2121,7 @@
 }
 
 /// EnumMatcher for 'match i32_texel_format'
-/// @see src/tint/intrinsics.def:151:7
+/// @see src/tint/intrinsics.def:155:7
 class I32TexelFormat : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
@@ -1994,7 +2153,7 @@
 }
 
 /// EnumMatcher for 'match u32_texel_format'
-/// @see src/tint/intrinsics.def:153:7
+/// @see src/tint/intrinsics.def:157:7
 class U32TexelFormat : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
@@ -2026,7 +2185,7 @@
 }
 
 /// EnumMatcher for 'match write_only'
-/// @see src/tint/intrinsics.def:156:7
+/// @see src/tint/intrinsics.def:160:7
 class WriteOnly : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
@@ -2052,7 +2211,7 @@
 }
 
 /// EnumMatcher for 'match function_private_workgroup'
-/// @see src/tint/intrinsics.def:158:7
+/// @see src/tint/intrinsics.def:162:7
 class FunctionPrivateWorkgroup : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
@@ -2082,7 +2241,7 @@
 }
 
 /// EnumMatcher for 'match workgroup_or_storage'
-/// @see src/tint/intrinsics.def:159:7
+/// @see src/tint/intrinsics.def:163:7
 class WorkgroupOrStorage : public NumberMatcher {
  public:
   /// Checks whether the given number matches the enum matcher rules.
@@ -2199,6 +2358,7 @@
   I32 I32_;
   U32 U32_;
   F32 F32_;
+  F16 F16_;
   Vec2 Vec2_;
   Vec3 Vec3_;
   Vec4 Vec4_;
@@ -2240,13 +2400,16 @@
   FrexpResult FrexpResult_;
   FrexpResultVec FrexpResultVec_;
   AtomicCompareExchangeResult AtomicCompareExchangeResult_;
+  F32F16 F32F16_;
   Fiu32 Fiu32_;
   Fi32 Fi32_;
   Iu32 Iu32_;
   Scalar Scalar_;
   AbstractOrScalar AbstractOrScalar_;
   AfF32 AfF32_;
+  AfF32F16 AfF32F16_;
   ScalarNoF32 ScalarNoF32_;
+  ScalarNoF16 ScalarNoF16_;
   ScalarNoI32 ScalarNoI32_;
   ScalarNoU32 ScalarNoU32_;
   ScalarNoBool ScalarNoBool_;
@@ -2267,7 +2430,7 @@
   ~Matchers();
 
   /// The template types, types, and type matchers
-  TypeMatcher const* const type[59] = {
+  TypeMatcher const* const type[63] = {
     /* [0] */ &template_type_0_,
     /* [1] */ &template_type_1_,
     /* [2] */ &Bool_,
@@ -2276,57 +2439,61 @@
     /* [5] */ &I32_,
     /* [6] */ &U32_,
     /* [7] */ &F32_,
-    /* [8] */ &Vec2_,
-    /* [9] */ &Vec3_,
-    /* [10] */ &Vec4_,
-    /* [11] */ &Mat2X2_,
-    /* [12] */ &Mat2X3_,
-    /* [13] */ &Mat2X4_,
-    /* [14] */ &Mat3X2_,
-    /* [15] */ &Mat3X3_,
-    /* [16] */ &Mat3X4_,
-    /* [17] */ &Mat4X2_,
-    /* [18] */ &Mat4X3_,
-    /* [19] */ &Mat4X4_,
-    /* [20] */ &Vec_,
-    /* [21] */ &Mat_,
-    /* [22] */ &Ptr_,
-    /* [23] */ &Atomic_,
-    /* [24] */ &Array_,
-    /* [25] */ &Sampler_,
-    /* [26] */ &SamplerComparison_,
-    /* [27] */ &Texture1D_,
-    /* [28] */ &Texture2D_,
-    /* [29] */ &Texture2DArray_,
-    /* [30] */ &Texture3D_,
-    /* [31] */ &TextureCube_,
-    /* [32] */ &TextureCubeArray_,
-    /* [33] */ &TextureMultisampled2D_,
-    /* [34] */ &TextureDepth2D_,
-    /* [35] */ &TextureDepth2DArray_,
-    /* [36] */ &TextureDepthCube_,
-    /* [37] */ &TextureDepthCubeArray_,
-    /* [38] */ &TextureDepthMultisampled2D_,
-    /* [39] */ &TextureStorage1D_,
-    /* [40] */ &TextureStorage2D_,
-    /* [41] */ &TextureStorage2DArray_,
-    /* [42] */ &TextureStorage3D_,
-    /* [43] */ &TextureExternal_,
-    /* [44] */ &ModfResult_,
-    /* [45] */ &ModfResultVec_,
-    /* [46] */ &FrexpResult_,
-    /* [47] */ &FrexpResultVec_,
-    /* [48] */ &AtomicCompareExchangeResult_,
-    /* [49] */ &Fiu32_,
-    /* [50] */ &Fi32_,
-    /* [51] */ &Iu32_,
-    /* [52] */ &Scalar_,
-    /* [53] */ &AbstractOrScalar_,
-    /* [54] */ &AfF32_,
-    /* [55] */ &ScalarNoF32_,
-    /* [56] */ &ScalarNoI32_,
-    /* [57] */ &ScalarNoU32_,
-    /* [58] */ &ScalarNoBool_,
+    /* [8] */ &F16_,
+    /* [9] */ &Vec2_,
+    /* [10] */ &Vec3_,
+    /* [11] */ &Vec4_,
+    /* [12] */ &Mat2X2_,
+    /* [13] */ &Mat2X3_,
+    /* [14] */ &Mat2X4_,
+    /* [15] */ &Mat3X2_,
+    /* [16] */ &Mat3X3_,
+    /* [17] */ &Mat3X4_,
+    /* [18] */ &Mat4X2_,
+    /* [19] */ &Mat4X3_,
+    /* [20] */ &Mat4X4_,
+    /* [21] */ &Vec_,
+    /* [22] */ &Mat_,
+    /* [23] */ &Ptr_,
+    /* [24] */ &Atomic_,
+    /* [25] */ &Array_,
+    /* [26] */ &Sampler_,
+    /* [27] */ &SamplerComparison_,
+    /* [28] */ &Texture1D_,
+    /* [29] */ &Texture2D_,
+    /* [30] */ &Texture2DArray_,
+    /* [31] */ &Texture3D_,
+    /* [32] */ &TextureCube_,
+    /* [33] */ &TextureCubeArray_,
+    /* [34] */ &TextureMultisampled2D_,
+    /* [35] */ &TextureDepth2D_,
+    /* [36] */ &TextureDepth2DArray_,
+    /* [37] */ &TextureDepthCube_,
+    /* [38] */ &TextureDepthCubeArray_,
+    /* [39] */ &TextureDepthMultisampled2D_,
+    /* [40] */ &TextureStorage1D_,
+    /* [41] */ &TextureStorage2D_,
+    /* [42] */ &TextureStorage2DArray_,
+    /* [43] */ &TextureStorage3D_,
+    /* [44] */ &TextureExternal_,
+    /* [45] */ &ModfResult_,
+    /* [46] */ &ModfResultVec_,
+    /* [47] */ &FrexpResult_,
+    /* [48] */ &FrexpResultVec_,
+    /* [49] */ &AtomicCompareExchangeResult_,
+    /* [50] */ &F32F16_,
+    /* [51] */ &Fiu32_,
+    /* [52] */ &Fi32_,
+    /* [53] */ &Iu32_,
+    /* [54] */ &Scalar_,
+    /* [55] */ &AbstractOrScalar_,
+    /* [56] */ &AfF32_,
+    /* [57] */ &AfF32F16_,
+    /* [58] */ &ScalarNoF32_,
+    /* [59] */ &ScalarNoF16_,
+    /* [60] */ &ScalarNoI32_,
+    /* [61] */ &ScalarNoU32_,
+    /* [62] */ &ScalarNoBool_,
   };
 
   /// The template numbers, and number matchers
@@ -2350,209 +2517,232 @@
 Matchers::~Matchers() = default;
 
 constexpr MatcherIndex kMatcherIndices[] = {
-  /* [0] */ 22,
+  /* [0] */ 23,
   /* [1] */ 0,
-  /* [2] */ 23,
+  /* [2] */ 24,
   /* [3] */ 0,
   /* [4] */ 11,
-  /* [5] */ 7,
-  /* [6] */ 22,
+  /* [5] */ 0,
+  /* [6] */ 23,
   /* [7] */ 9,
-  /* [8] */ 24,
+  /* [8] */ 25,
   /* [9] */ 0,
   /* [10] */ 0,
-  /* [11] */ 21,
+  /* [11] */ 22,
   /* [12] */ 0,
   /* [13] */ 1,
   /* [14] */ 7,
-  /* [15] */ 21,
+  /* [15] */ 22,
   /* [16] */ 0,
-  /* [17] */ 0,
+  /* [17] */ 2,
   /* [18] */ 7,
-  /* [19] */ 21,
-  /* [20] */ 0,
-  /* [21] */ 2,
+  /* [19] */ 22,
+  /* [20] */ 1,
+  /* [21] */ 0,
   /* [22] */ 7,
-  /* [23] */ 21,
+  /* [23] */ 22,
   /* [24] */ 1,
-  /* [25] */ 0,
+  /* [25] */ 2,
   /* [26] */ 7,
-  /* [27] */ 21,
-  /* [28] */ 1,
-  /* [29] */ 2,
+  /* [27] */ 22,
+  /* [28] */ 0,
+  /* [29] */ 0,
   /* [30] */ 7,
-  /* [31] */ 20,
+  /* [31] */ 21,
   /* [32] */ 0,
-  /* [33] */ 7,
-  /* [34] */ 41,
+  /* [33] */ 0,
+  /* [34] */ 21,
   /* [35] */ 0,
-  /* [36] */ 1,
-  /* [37] */ 20,
-  /* [38] */ 0,
-  /* [39] */ 0,
-  /* [40] */ 42,
-  /* [41] */ 5,
-  /* [42] */ 10,
-  /* [43] */ 7,
-  /* [44] */ 20,
-  /* [45] */ 0,
-  /* [46] */ 2,
-  /* [47] */ 41,
-  /* [48] */ 5,
-  /* [49] */ 10,
-  /* [50] */ 0,
-  /* [51] */ 40,
-  /* [52] */ 5,
-  /* [53] */ 10,
-  /* [54] */ 1,
-  /* [55] */ 39,
-  /* [56] */ 5,
-  /* [57] */ 10,
-  /* [58] */ 5,
-  /* [59] */ 42,
-  /* [60] */ 4,
-  /* [61] */ 10,
-  /* [62] */ 6,
-  /* [63] */ 41,
+  /* [36] */ 7,
+  /* [37] */ 43,
+  /* [38] */ 5,
+  /* [39] */ 10,
+  /* [40] */ 7,
+  /* [41] */ 42,
+  /* [42] */ 5,
+  /* [43] */ 10,
+  /* [44] */ 2,
+  /* [45] */ 21,
+  /* [46] */ 0,
+  /* [47] */ 2,
+  /* [48] */ 41,
+  /* [49] */ 5,
+  /* [50] */ 10,
+  /* [51] */ 1,
+  /* [52] */ 40,
+  /* [53] */ 5,
+  /* [54] */ 10,
+  /* [55] */ 6,
+  /* [56] */ 43,
+  /* [57] */ 4,
+  /* [58] */ 10,
+  /* [59] */ 5,
+  /* [60] */ 42,
+  /* [61] */ 0,
+  /* [62] */ 1,
+  /* [63] */ 42,
   /* [64] */ 4,
   /* [65] */ 10,
-  /* [66] */ 2,
-  /* [67] */ 40,
+  /* [66] */ 8,
+  /* [67] */ 41,
   /* [68] */ 4,
   /* [69] */ 10,
-  /* [70] */ 39,
-  /* [71] */ 4,
-  /* [72] */ 10,
-  /* [73] */ 42,
-  /* [74] */ 3,
-  /* [75] */ 10,
-  /* [76] */ 20,
-  /* [77] */ 1,
-  /* [78] */ 7,
-  /* [79] */ 41,
-  /* [80] */ 3,
-  /* [81] */ 10,
-  /* [82] */ 40,
-  /* [83] */ 3,
-  /* [84] */ 10,
-  /* [85] */ 42,
-  /* [86] */ 0,
-  /* [87] */ 1,
-  /* [88] */ 40,
-  /* [89] */ 0,
-  /* [90] */ 1,
-  /* [91] */ 39,
-  /* [92] */ 0,
-  /* [93] */ 1,
-  /* [94] */ 20,
-  /* [95] */ 0,
-  /* [96] */ 5,
-  /* [97] */ 39,
-  /* [98] */ 3,
-  /* [99] */ 10,
-  /* [100] */ 20,
-  /* [101] */ 0,
-  /* [102] */ 6,
-  /* [103] */ 28,
-  /* [104] */ 7,
-  /* [105] */ 8,
-  /* [106] */ 0,
-  /* [107] */ 8,
-  /* [108] */ 1,
-  /* [109] */ 8,
-  /* [110] */ 7,
-  /* [111] */ 8,
-  /* [112] */ 5,
-  /* [113] */ 8,
-  /* [114] */ 6,
-  /* [115] */ 8,
-  /* [116] */ 2,
-  /* [117] */ 9,
-  /* [118] */ 0,
-  /* [119] */ 45,
-  /* [120] */ 0,
-  /* [121] */ 9,
-  /* [122] */ 1,
-  /* [123] */ 9,
-  /* [124] */ 7,
-  /* [125] */ 9,
-  /* [126] */ 5,
-  /* [127] */ 9,
-  /* [128] */ 6,
-  /* [129] */ 9,
-  /* [130] */ 2,
-  /* [131] */ 30,
-  /* [132] */ 0,
-  /* [133] */ 27,
-  /* [134] */ 0,
-  /* [135] */ 28,
-  /* [136] */ 0,
-  /* [137] */ 29,
-  /* [138] */ 0,
-  /* [139] */ 31,
-  /* [140] */ 0,
-  /* [141] */ 32,
-  /* [142] */ 0,
-  /* [143] */ 33,
-  /* [144] */ 0,
-  /* [145] */ 47,
-  /* [146] */ 0,
-  /* [147] */ 11,
-  /* [148] */ 0,
-  /* [149] */ 12,
-  /* [150] */ 7,
-  /* [151] */ 12,
-  /* [152] */ 0,
-  /* [153] */ 13,
-  /* [154] */ 7,
-  /* [155] */ 13,
-  /* [156] */ 0,
-  /* [157] */ 14,
-  /* [158] */ 7,
-  /* [159] */ 14,
-  /* [160] */ 0,
-  /* [161] */ 15,
-  /* [162] */ 7,
-  /* [163] */ 15,
-  /* [164] */ 0,
-  /* [165] */ 16,
-  /* [166] */ 7,
-  /* [167] */ 16,
-  /* [168] */ 0,
-  /* [169] */ 17,
-  /* [170] */ 7,
-  /* [171] */ 17,
-  /* [172] */ 0,
-  /* [173] */ 48,
-  /* [174] */ 0,
-  /* [175] */ 18,
-  /* [176] */ 7,
-  /* [177] */ 18,
-  /* [178] */ 0,
-  /* [179] */ 27,
-  /* [180] */ 7,
-  /* [181] */ 29,
-  /* [182] */ 7,
-  /* [183] */ 30,
-  /* [184] */ 7,
-  /* [185] */ 31,
-  /* [186] */ 7,
-  /* [187] */ 32,
-  /* [188] */ 7,
-  /* [189] */ 19,
-  /* [190] */ 7,
-  /* [191] */ 19,
-  /* [192] */ 0,
-  /* [193] */ 25,
-  /* [194] */ 26,
-  /* [195] */ 37,
-  /* [196] */ 36,
-  /* [197] */ 35,
-  /* [198] */ 34,
-  /* [199] */ 43,
-  /* [200] */ 38,
-  /* [201] */ 44,
-  /* [202] */ 46,
+  /* [70] */ 0,
+  /* [71] */ 40,
+  /* [72] */ 4,
+  /* [73] */ 10,
+  /* [74] */ 43,
+  /* [75] */ 3,
+  /* [76] */ 10,
+  /* [77] */ 21,
+  /* [78] */ 1,
+  /* [79] */ 7,
+  /* [80] */ 42,
+  /* [81] */ 3,
+  /* [82] */ 10,
+  /* [83] */ 41,
+  /* [84] */ 3,
+  /* [85] */ 10,
+  /* [86] */ 43,
+  /* [87] */ 0,
+  /* [88] */ 1,
+  /* [89] */ 41,
+  /* [90] */ 0,
+  /* [91] */ 1,
+  /* [92] */ 40,
+  /* [93] */ 0,
+  /* [94] */ 1,
+  /* [95] */ 21,
+  /* [96] */ 0,
+  /* [97] */ 5,
+  /* [98] */ 21,
+  /* [99] */ 0,
+  /* [100] */ 6,
+  /* [101] */ 40,
+  /* [102] */ 3,
+  /* [103] */ 10,
+  /* [104] */ 9,
+  /* [105] */ 0,
+  /* [106] */ 11,
+  /* [107] */ 7,
+  /* [108] */ 9,
+  /* [109] */ 7,
+  /* [110] */ 9,
+  /* [111] */ 2,
+  /* [112] */ 9,
+  /* [113] */ 1,
+  /* [114] */ 46,
+  /* [115] */ 0,
+  /* [116] */ 11,
+  /* [117] */ 1,
+  /* [118] */ 9,
+  /* [119] */ 6,
+  /* [120] */ 11,
+  /* [121] */ 8,
+  /* [122] */ 9,
+  /* [123] */ 5,
+  /* [124] */ 9,
+  /* [125] */ 8,
+  /* [126] */ 28,
+  /* [127] */ 0,
+  /* [128] */ 29,
+  /* [129] */ 0,
+  /* [130] */ 30,
+  /* [131] */ 0,
+  /* [132] */ 31,
+  /* [133] */ 0,
+  /* [134] */ 32,
+  /* [135] */ 0,
+  /* [136] */ 11,
+  /* [137] */ 5,
+  /* [138] */ 33,
+  /* [139] */ 0,
+  /* [140] */ 34,
+  /* [141] */ 0,
+  /* [142] */ 11,
+  /* [143] */ 6,
+  /* [144] */ 11,
+  /* [145] */ 2,
+  /* [146] */ 12,
+  /* [147] */ 0,
+  /* [148] */ 12,
+  /* [149] */ 7,
+  /* [150] */ 12,
+  /* [151] */ 8,
+  /* [152] */ 13,
+  /* [153] */ 0,
+  /* [154] */ 20,
+  /* [155] */ 7,
+  /* [156] */ 13,
+  /* [157] */ 7,
+  /* [158] */ 13,
+  /* [159] */ 8,
+  /* [160] */ 14,
+  /* [161] */ 0,
+  /* [162] */ 48,
+  /* [163] */ 0,
+  /* [164] */ 14,
+  /* [165] */ 7,
+  /* [166] */ 14,
+  /* [167] */ 8,
+  /* [168] */ 15,
+  /* [169] */ 0,
+  /* [170] */ 15,
+  /* [171] */ 7,
+  /* [172] */ 15,
+  /* [173] */ 8,
+  /* [174] */ 16,
+  /* [175] */ 0,
+  /* [176] */ 16,
+  /* [177] */ 7,
+  /* [178] */ 16,
+  /* [179] */ 8,
+  /* [180] */ 17,
+  /* [181] */ 0,
+  /* [182] */ 17,
+  /* [183] */ 7,
+  /* [184] */ 17,
+  /* [185] */ 8,
+  /* [186] */ 18,
+  /* [187] */ 0,
+  /* [188] */ 18,
+  /* [189] */ 7,
+  /* [190] */ 18,
+  /* [191] */ 8,
+  /* [192] */ 28,
+  /* [193] */ 7,
+  /* [194] */ 29,
+  /* [195] */ 7,
+  /* [196] */ 19,
+  /* [197] */ 0,
+  /* [198] */ 30,
+  /* [199] */ 7,
+  /* [200] */ 49,
+  /* [201] */ 0,
+  /* [202] */ 31,
+  /* [203] */ 7,
+  /* [204] */ 32,
+  /* [205] */ 7,
+  /* [206] */ 33,
+  /* [207] */ 7,
+  /* [208] */ 19,
+  /* [209] */ 7,
+  /* [210] */ 19,
+  /* [211] */ 8,
+  /* [212] */ 20,
+  /* [213] */ 0,
+  /* [214] */ 20,
+  /* [215] */ 8,
+  /* [216] */ 26,
+  /* [217] */ 27,
+  /* [218] */ 38,
+  /* [219] */ 37,
+  /* [220] */ 36,
+  /* [221] */ 35,
+  /* [222] */ 44,
+  /* [223] */ 39,
+  /* [224] */ 45,
+  /* [225] */ 47,
 };
 
 // Assert that the MatcherIndex is big enough to index all the matchers, plus
@@ -2890,1152 +3080,1152 @@
   {
     /* [65] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [66] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [67] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [68] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [69] */
     /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [70] */
     /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [71] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [72] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [73] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [74] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [75] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [76] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [77] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [78] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [79] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [80] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [81] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [82] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [83] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [84] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [85] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [86] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [87] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [88] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [89] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [90] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [91] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [92] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [93] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [94] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [95] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [96] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [97] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [98] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [99] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [100] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [101] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [102] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[187],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [103] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [104] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [105] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [106] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [107] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [108] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [109] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [110] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [111] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [112] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [113] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [114] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [115] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [116] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [117] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [118] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [119] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [120] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [121] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [122] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [123] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [124] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [125] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [126] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [127] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [128] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [129] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [130] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [131] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [132] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [133] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [134] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [135] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [136] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [137] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [138] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [139] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [140] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [141] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [142] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [143] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [144] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[206],
   },
   {
     /* [145] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [146] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [147] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [148] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [149] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [150] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [151] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[135],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [152] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [153] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [154] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [155] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [156] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [157] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [158] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [159] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [160] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [161] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [162] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [163] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [164] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [165] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [166] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [167] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [168] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [169] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [170] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* matcher indices */ &kMatcherIndices[206],
   },
   {
     /* [171] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [172] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [173] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [174] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [175] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[206],
   },
   {
     /* [176] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [177] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [178] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [179] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [180] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [181] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [182] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [183] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [184] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [185] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [186] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [187] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [188] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [189] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [190] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [191] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [192] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [193] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [194] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [195] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [196] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [197] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [198] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [199] */
     /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [200] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [201] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [202] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [203] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [204] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [205] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [206] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [207] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [208] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [209] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [210] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [211] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[141],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [212] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [213] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [214] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [215] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [216] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [217] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [218] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [219] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [220] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [221] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [222] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [223] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [224] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [225] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [226] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [227] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [228] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [229] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [230] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[185],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [231] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [232] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [233] */
-    /* usage */ ParameterUsage::kDdx,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [234] */
-    /* usage */ ParameterUsage::kDdy,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [235] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[187],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [236] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [237] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [238] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [239] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [240] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [241] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [242] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [243] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [244] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [245] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[198],
   },
   {
     /* [246] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [247] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [248] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [249] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [250] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [251] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [252] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [253] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [254] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [255] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [256] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [257] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [258] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [259] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [260] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [261] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [262] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [263] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [264] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [265] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [266] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [267] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [268] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [269] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [270] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [271] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [272] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [273] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [274] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [275] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[187],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [276] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [277] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [278] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [279] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [280] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [281] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [282] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [283] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [284] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [285] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[204],
   },
   {
     /* [286] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [287] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [288] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDdx,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [289] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kDdy,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [290] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [291] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [292] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [293] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [294] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [295] */
@@ -4045,637 +4235,637 @@
   {
     /* [296] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [297] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [298] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [299] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[185],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [300] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [301] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [302] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [303] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [304] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [305] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [306] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [307] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [308] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [309] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [310] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [311] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[181],
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [312] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [313] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [314] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [315] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [316] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [317] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [318] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [319] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [320] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [321] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [322] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [323] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[206],
   },
   {
     /* [324] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [325] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [326] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [327] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[79],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [328] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [329] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [330] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[42],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [331] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [332] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [333] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [334] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [335] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [336] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [337] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [338] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kOffset,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [339] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[187],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [340] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [341] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [342] */
     /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [343] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [344] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [345] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [346] */
     /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [347] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[63],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [348] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [349] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [350] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[57],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [351] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [352] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [353] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [354] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [355] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [356] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [357] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [358] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [359] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[185],
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [360] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [361] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [362] */
-    /* usage */ ParameterUsage::kBias,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [363] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [364] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [365] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [366] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [367] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[47],
+    /* usage */ ParameterUsage::kComponent,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [368] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [369] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [370] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[61],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [371] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [372] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [373] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [374] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [375] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [376] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [377] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [378] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [379] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [380] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [381] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [382] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [383] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* matcher indices */ &kMatcherIndices[204],
   },
   {
     /* [384] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [385] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [386] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [387] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[204],
   },
   {
     /* [388] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [389] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [390] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kBias,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [391] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [392] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [393] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [394] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [395] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [396] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [397] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [398] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [399] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [400] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [401] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [402] */
-    /* usage */ ParameterUsage::kArrayIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [403] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [404] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [405] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [406] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [407] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [408] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [409] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [410] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [411] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [412] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [413] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [414] */
-    /* usage */ ParameterUsage::kOffset,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [415] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* matcher indices */ &kMatcherIndices[80],
   },
   {
     /* [416] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [417] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [418] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [419] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[63],
   },
   {
     /* [420] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [421] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [422] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[136],
   },
   {
     /* [423] */
@@ -4690,202 +4880,202 @@
   {
     /* [425] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [426] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [427] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [428] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [429] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [430] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [431] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[41],
   },
   {
     /* [432] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[194],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [433] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [434] */
-    /* usage */ ParameterUsage::kDepthRef,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [435] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [436] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [437] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kArrayIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [438] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [439] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kX,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [440] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kY,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [441] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [442] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [443] */
-    /* usage */ ParameterUsage::kComponent,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [444] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[135],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[217],
   },
   {
     /* [445] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [446] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kDepthRef,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [447] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [448] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [449] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kSampleIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [450] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [451] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [452] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [453] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[71],
   },
   {
     /* [454] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [455] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[136],
   },
   {
     /* [456] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [457] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [458] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [459] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [460] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [461] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [462] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [463] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [464] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [465] */
@@ -4900,897 +5090,897 @@
   {
     /* [467] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [468] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [469] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [470] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [471] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[199],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [472] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [473] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [474] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [475] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [476] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [477] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[97],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [478] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [479] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[42],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [480] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[82],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [481] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [482] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[42],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [483] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[73],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [484] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [485] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[42],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [486] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [487] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [488] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kW,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [489] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[70],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [490] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [491] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[57],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [492] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[185],
+    /* matcher indices */ &kMatcherIndices[204],
   },
   {
     /* [493] */
     /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [494] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [495] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[67],
+    /* matcher indices */ &kMatcherIndices[202],
   },
   {
     /* [496] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [497] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[57],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [498] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[183],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [499] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [500] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kZ,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [501] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[59],
+    /* matcher indices */ &kMatcherIndices[194],
   },
   {
     /* [502] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [503] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[57],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [504] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[55],
+    /* matcher indices */ &kMatcherIndices[192],
   },
   {
     /* [505] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [506] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[61],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [507] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [508] */
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
+  },
+  {
+    /* [509] */
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[39],
+  },
+  {
+    /* [510] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [511] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [512] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[17],
+  },
+  {
+    /* [513] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [514] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[55],
+  },
+  {
+    /* [515] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[55],
+  },
+  {
+    /* [516] */
     /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[31],
   },
   {
-    /* [509] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
-  },
-  {
-    /* [510] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[51],
-  },
-  {
-    /* [511] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
-  },
-  {
-    /* [512] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[61],
-  },
-  {
-    /* [513] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[103],
-  },
-  {
-    /* [514] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
-  },
-  {
-    /* [515] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
-  },
-  {
-    /* [516] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[179],
-  },
-  {
     /* [517] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [518] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [519] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[40],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [520] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [521] */
-    /* usage */ ParameterUsage::kValue,
-    /* matcher indices */ &kMatcherIndices[61],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [522] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [523] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [524] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [525] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [526] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [527] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [528] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [529] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [530] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [531] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [532] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [533] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [534] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[135],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [535] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [536] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [537] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [538] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kSampler,
+    /* matcher indices */ &kMatcherIndices[216],
   },
   {
     /* [539] */
-    /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [540] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* matcher indices */ &kMatcherIndices[101],
   },
   {
     /* [541] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [542] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [543] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[200],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [544] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [545] */
-    /* usage */ ParameterUsage::kSampleIndex,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [546] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [547] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [548] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [549] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[83],
   },
   {
     /* [550] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [551] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [552] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[74],
   },
   {
     /* [553] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [554] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [555] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* matcher indices */ &kMatcherIndices[67],
   },
   {
     /* [556] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [557] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[136],
   },
   {
     /* [558] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[56],
   },
   {
     /* [559] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [560] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[136],
   },
   {
     /* [561] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[52],
   },
   {
     /* [562] */
-    /* usage */ ParameterUsage::kZ,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [563] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [564] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[48],
   },
   {
     /* [565] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [566] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [567] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [568] */
-    /* usage */ ParameterUsage::kSampler,
-    /* matcher indices */ &kMatcherIndices[193],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [569] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [570] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [571] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [572] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [573] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[37],
   },
   {
     /* [574] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [575] */
-    /* usage */ ParameterUsage::kZw,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kValue,
+    /* matcher indices */ &kMatcherIndices[142],
   },
   {
     /* [576] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[223],
   },
   {
     /* [577] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [578] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kSampleIndex,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [579] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [580] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [581] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [582] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [583] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [584] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [585] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [586] */
-    /* usage */ ParameterUsage::kYz,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[58],
   },
   {
     /* [587] */
-    /* usage */ ParameterUsage::kW,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [588] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [589] */
     /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[125],
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [590] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [591] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [592] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [593] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [594] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [595] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* matcher indices */ &kMatcherIndices[77],
   },
   {
     /* [596] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [597] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [598] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [599] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [600] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [601] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [602] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kCoords,
+    /* matcher indices */ &kMatcherIndices[122],
   },
   {
     /* [603] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[15],
   },
   {
     /* [604] */
-    /* usage */ ParameterUsage::kZyw,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[19],
   },
   {
     /* [605] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [606] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [607] */
-    /* usage */ ParameterUsage::kXyz,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [608] */
-    /* usage */ ParameterUsage::kW,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [609] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[135],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [610] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
-  },
-  {
-    /* [611] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[105],
-  },
-  {
-    /* [612] */
-    /* usage */ ParameterUsage::kZw,
-    /* matcher indices */ &kMatcherIndices[105],
-  },
-  {
-    /* [613] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
-  },
-  {
-    /* [614] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
-  },
-  {
-    /* [615] */
-    /* usage */ ParameterUsage::kX,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
+    /* [611] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [612] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [613] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
+  },
+  {
+    /* [614] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [615] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
+  },
+  {
     /* [616] */
-    /* usage */ ParameterUsage::kYz,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [617] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [618] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [619] */
-    /* usage */ ParameterUsage::kXy,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [620] */
-    /* usage */ ParameterUsage::kZ,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [621] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [622] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [623] */
-    /* usage */ ParameterUsage::kX,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [624] */
-    /* usage */ ParameterUsage::kY,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [625] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[141],
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [626] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [627] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [628] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[100],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [629] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [630] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [631] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [632] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[95],
   },
   {
     /* [633] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [634] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[100],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [635] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [636] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
-  },
-  {
-    /* [637] */
     /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
+    /* [637] */
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
+  },
+  {
     /* [638] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [639] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [640] */
-    /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [641] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [642] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [643] */
     /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [644] */
     /* usage */ ParameterUsage::kLevel,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [645] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [646] */
@@ -5799,38 +5989,38 @@
   },
   {
     /* [647] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [648] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [649] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [650] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [651] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [652] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [653] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [654] */
@@ -5839,13 +6029,13 @@
   },
   {
     /* [655] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [656] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kLevel,
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [657] */
@@ -5855,167 +6045,167 @@
   {
     /* [658] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [659] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [660] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [661] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [662] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [663] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [664] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [665] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [666] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [667] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [668] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [669] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [670] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [671] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [672] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [673] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [674] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [675] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [676] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [677] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [678] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [679] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [680] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [681] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [682] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [683] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [684] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [685] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [686] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [687] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [688] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [689] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [690] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [691] */
@@ -6030,32 +6220,32 @@
   {
     /* [693] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [694] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [695] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [696] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [697] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [698] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [699] */
@@ -6070,17 +6260,17 @@
   {
     /* [701] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [702] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [703] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [704] */
@@ -6090,12 +6280,12 @@
   {
     /* [705] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [706] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [707] */
@@ -6110,27 +6300,27 @@
   {
     /* [709] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[19],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [710] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[23],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [711] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[76],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [712] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [713] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [714] */
@@ -6140,92 +6330,92 @@
   {
     /* [715] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [716] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [717] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [718] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [719] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [720] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [721] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [722] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [723] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [724] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [725] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [726] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [727] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [728] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [729] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [730] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [731] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [732] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [733] */
@@ -6235,182 +6425,182 @@
   {
     /* [734] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[98],
   },
   {
     /* [735] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [736] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [737] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [738] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[39],
   },
   {
     /* [739] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [740] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [741] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [742] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [743] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [744] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[98],
   },
   {
     /* [745] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kX,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [746] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kY,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [747] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [748] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [749] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [750] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
-  },
-  {
-    /* [751] */
-    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
+    /* [751] */
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[104],
+  },
+  {
     /* [752] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kZ,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [753] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [754] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [755] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [756] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kYz,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [757] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [758] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [759] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [760] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [761] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [762] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [763] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [764] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [765] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [766] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [767] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* usage */ ParameterUsage::kXy,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [768] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kZw,
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [769] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [770] */
@@ -6420,97 +6610,97 @@
   {
     /* [771] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [772] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [773] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* usage */ ParameterUsage::kXyz,
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [774] */
-    /* usage */ ParameterUsage::kNone,
+    /* usage */ ParameterUsage::kW,
     /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [775] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* usage */ ParameterUsage::kX,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [776] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kZyw,
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [777] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [778] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [779] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [780] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[94],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [781] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [782] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [783] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[199],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [784] */
-    /* usage */ ParameterUsage::kCoords,
-    /* matcher indices */ &kMatcherIndices[111],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [785] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [786] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[123],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [787] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [788] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [789] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [790] */
@@ -6520,317 +6710,317 @@
   {
     /* [791] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [792] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [793] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [794] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [795] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [796] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [797] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[223],
   },
   {
     /* [798] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [799] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [800] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [801] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [802] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [803] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [804] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [805] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [806] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [807] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [808] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [809] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [810] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [811] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [812] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [813] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [814] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[222],
   },
   {
     /* [815] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[86],
   },
   {
     /* [816] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[60],
   },
   {
     /* [817] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[89],
   },
   {
     /* [818] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[92],
   },
   {
     /* [819] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[223],
   },
   {
     /* [820] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[218],
   },
   {
     /* [821] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[219],
   },
   {
     /* [822] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[220],
   },
   {
     /* [823] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[221],
   },
   {
     /* [824] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[140],
   },
   {
     /* [825] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[138],
   },
   {
     /* [826] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[134],
   },
   {
     /* [827] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[132],
   },
   {
     /* [828] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[130],
   },
   {
     /* [829] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[15],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[128],
   },
   {
     /* [830] */
-    /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* usage */ ParameterUsage::kTexture,
+    /* matcher indices */ &kMatcherIndices[126],
   },
   {
     /* [831] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[154],
   },
   {
     /* [832] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [833] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [834] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [835] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[85],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [836] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [837] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [838] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [839] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[11],
   },
   {
     /* [840] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [841] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[0],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [842] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [843] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [844] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [845] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [846] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [847] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[0],
   },
   {
     /* [848] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [849] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [850] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [851] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [852] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [853] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [854] */
@@ -6840,212 +7030,212 @@
   {
     /* [855] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [856] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [857] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [858] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [859] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[45],
   },
   {
     /* [860] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[109],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [861] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[42],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [862] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[42],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [863] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [864] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [865] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [866] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [867] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [868] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[200],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[106],
   },
   {
     /* [869] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [870] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [871] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[108],
   },
   {
     /* [872] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
-  },
-  {
-    /* [873] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
-  },
-  {
-    /* [874] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[141],
-  },
-  {
-    /* [875] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
-  },
-  {
-    /* [876] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
-  },
-  {
-    /* [877] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
-  },
-  {
-    /* [878] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[135],
-  },
-  {
-    /* [879] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
-  },
-  {
-    /* [880] */
-    /* usage */ ParameterUsage::kTexture,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[34],
   },
   {
+    /* [873] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [874] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
+    /* [875] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [876] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
+    /* [877] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [878] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
+    /* [879] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [880] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
     /* [881] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [882] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [883] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[141],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [884] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [885] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [886] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [887] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [888] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [889] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [890] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [891] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [892] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [893] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [894] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [895] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [896] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [897] */
@@ -7055,117 +7245,117 @@
   {
     /* [898] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [899] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [900] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[11],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [901] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [902] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[199],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [903] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [904] */
-    /* usage */ ParameterUsage::kTexture,
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [905] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[88],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [906] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[91],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [907] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[200],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [908] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[195],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [909] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[196],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [910] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[197],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [911] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[198],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [912] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[143],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [913] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[189],
+    /* matcher indices */ &kMatcherIndices[214],
   },
   {
     /* [914] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[41],
+    /* matcher indices */ &kMatcherIndices[184],
   },
   {
     /* [915] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[27],
   },
   {
     /* [916] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [917] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [918] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[31],
   },
   {
     /* [919] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [920] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[38],
   },
   {
     /* [921] */
@@ -7180,7 +7370,7 @@
   {
     /* [923] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[55],
   },
   {
     /* [924] */
@@ -7190,12 +7380,12 @@
   {
     /* [925] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[175],
+    /* matcher indices */ &kMatcherIndices[212],
   },
   {
     /* [926] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[105],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [927] */
@@ -7204,88 +7394,88 @@
   },
   {
     /* [928] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[141],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[210],
   },
   {
     /* [929] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* matcher indices */ &kMatcherIndices[66],
   },
   {
     /* [930] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [931] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* matcher indices */ &kMatcherIndices[208],
   },
   {
     /* [932] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[107],
+    /* matcher indices */ &kMatcherIndices[17],
   },
   {
     /* [933] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [934] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[117],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [935] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[104],
   },
   {
     /* [936] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [937] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[139],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [938] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[131],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [939] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [940] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [941] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [942] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[121],
+    /* matcher indices */ &kMatcherIndices[112],
   },
   {
     /* [943] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [944] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[49],
+    /* matcher indices */ &kMatcherIndices[69],
   },
   {
     /* [945] */
@@ -7300,7 +7490,7 @@
   {
     /* [947] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [948] */
@@ -7310,42 +7500,42 @@
   {
     /* [949] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[50],
   },
   {
     /* [950] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[137],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[50],
   },
   {
     /* [951] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[135],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[50],
   },
   {
     /* [952] */
-    /* usage */ ParameterUsage::kTexture,
-    /* matcher indices */ &kMatcherIndices[133],
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[50],
   },
   {
     /* [953] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[53],
+    /* matcher indices */ &kMatcherIndices[50],
   },
   {
     /* [954] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[53],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [955] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[53],
+    /* matcher indices */ &kMatcherIndices[4],
   },
   {
     /* [956] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[53],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [957] */
@@ -7355,133 +7545,278 @@
   {
     /* [958] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[4],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [959] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [960] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[37],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [961] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [962] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[169],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [963] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[149],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [964] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[116],
   },
   {
     /* [965] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[116],
   },
   {
     /* [966] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[116],
   },
   {
     /* [967] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[21],
+    /* matcher indices */ &kMatcherIndices[116],
   },
   {
     /* [968] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[153],
+    /* matcher indices */ &kMatcherIndices[116],
   },
   {
     /* [969] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[196],
   },
   {
     /* [970] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[146],
   },
   {
     /* [971] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[62],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [972] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[44],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [973] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[157],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [974] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[148],
   },
   {
     /* [975] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[150],
   },
   {
     /* [976] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[31],
+    /* matcher indices */ &kMatcherIndices[190],
   },
   {
     /* [977] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[152],
   },
   {
     /* [978] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[161],
+    /* matcher indices */ &kMatcherIndices[1],
   },
   {
     /* [979] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[1],
+    /* matcher indices */ &kMatcherIndices[34],
   },
   {
     /* [980] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[5],
+    /* matcher indices */ &kMatcherIndices[14],
   },
   {
     /* [981] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[6],
+    /* matcher indices */ &kMatcherIndices[156],
   },
   {
     /* [982] */
     /* usage */ ParameterUsage::kNone,
-    /* matcher indices */ &kMatcherIndices[165],
+    /* matcher indices */ &kMatcherIndices[158],
   },
   {
     /* [983] */
     /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[188],
+  },
+  {
+    /* [984] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[160],
+  },
+  {
+    /* [985] */
+    /* usage */ ParameterUsage::kNone,
     /* matcher indices */ &kMatcherIndices[1],
   },
+  {
+    /* [986] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [987] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
+    /* [988] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[164],
+  },
+  {
+    /* [989] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[166],
+  },
+  {
+    /* [990] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [991] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[168],
+  },
+  {
+    /* [992] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [993] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
+  {
+    /* [994] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
+    /* [995] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[170],
+  },
+  {
+    /* [996] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[172],
+  },
+  {
+    /* [997] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[17],
+  },
+  {
+    /* [998] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[174],
+  },
+  {
+    /* [999] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [1000] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[6],
+  },
+  {
+    /* [1001] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[45],
+  },
+  {
+    /* [1002] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[176],
+  },
+  {
+    /* [1003] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[178],
+  },
+  {
+    /* [1004] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [1005] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[180],
+  },
+  {
+    /* [1006] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[1],
+  },
+  {
+    /* [1007] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[17],
+  },
+  {
+    /* [1008] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[45],
+  },
+  {
+    /* [1009] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[182],
+  },
+  {
+    /* [1010] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[14],
+  },
+  {
+    /* [1011] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[186],
+  },
+  {
+    /* [1012] */
+    /* usage */ ParameterUsage::kNone,
+    /* matcher indices */ &kMatcherIndices[34],
+  },
 };
 
 constexpr TemplateTypeInfo kTemplateTypes[] = {
@@ -7493,7 +7828,7 @@
   {
     /* [1] */
     /* name */ "U",
-    /* matcher index */ 58,
+    /* matcher index */ 62,
   },
   {
     /* [2] */
@@ -7503,87 +7838,107 @@
   {
     /* [3] */
     /* name */ "U",
-    /* matcher index */ 55,
+    /* matcher index */ 58,
   },
   {
     /* [4] */
     /* name */ "T",
-    /* matcher index */ 5,
+    /* matcher index */ 8,
   },
   {
     /* [5] */
     /* name */ "U",
-    /* matcher index */ 56,
+    /* matcher index */ 59,
   },
   {
     /* [6] */
     /* name */ "T",
-    /* matcher index */ 6,
+    /* matcher index */ 5,
   },
   {
     /* [7] */
     /* name */ "U",
-    /* matcher index */ 57,
+    /* matcher index */ 60,
   },
   {
     /* [8] */
     /* name */ "T",
-    /* matcher index */ 49,
+    /* matcher index */ 6,
   },
   {
     /* [9] */
-    /* name */ "f32",
-    /* matcher index */ kNoMatcher,
+    /* name */ "U",
+    /* matcher index */ 61,
   },
   {
     /* [10] */
     /* name */ "T",
-    /* matcher index */ 54,
+    /* matcher index */ 53,
   },
   {
     /* [11] */
     /* name */ "T",
-    /* matcher index */ 51,
+    /* matcher index */ 57,
   },
   {
     /* [12] */
     /* name */ "T",
-    /* matcher index */ 53,
+    /* matcher index */ 56,
   },
   {
     /* [13] */
     /* name */ "T",
-    /* matcher index */ 52,
+    /* matcher index */ 50,
   },
   {
     /* [14] */
     /* name */ "T",
-    /* matcher index */ kNoMatcher,
+    /* matcher index */ 51,
   },
   {
     /* [15] */
     /* name */ "T",
-    /* matcher index */ 58,
+    /* matcher index */ 55,
   },
   {
     /* [16] */
     /* name */ "T",
-    /* matcher index */ 55,
+    /* matcher index */ 54,
   },
   {
     /* [17] */
     /* name */ "T",
-    /* matcher index */ 57,
+    /* matcher index */ kNoMatcher,
   },
   {
     /* [18] */
     /* name */ "T",
-    /* matcher index */ 56,
+    /* matcher index */ 59,
   },
   {
     /* [19] */
     /* name */ "T",
-    /* matcher index */ 50,
+    /* matcher index */ 62,
+  },
+  {
+    /* [20] */
+    /* name */ "T",
+    /* matcher index */ 58,
+  },
+  {
+    /* [21] */
+    /* name */ "T",
+    /* matcher index */ 61,
+  },
+  {
+    /* [22] */
+    /* name */ "T",
+    /* matcher index */ 60,
+  },
+  {
+    /* [23] */
+    /* name */ "T",
+    /* matcher index */ 52,
   },
 };
 
@@ -7646,10 +8001,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[952],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[830],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7658,10 +8013,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[605],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[617],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7670,10 +8025,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[951],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[829],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7682,10 +8037,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[609],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[621],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7694,10 +8049,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[950],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[828],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7706,10 +8061,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[613],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[625],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7718,10 +8073,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[938],
-    /* return matcher indices */ &kMatcherIndices[125],
+    /* parameters */ &kParameters[827],
+    /* return matcher indices */ &kMatcherIndices[58],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7730,10 +8085,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[617],
-    /* return matcher indices */ &kMatcherIndices[125],
+    /* parameters */ &kParameters[629],
+    /* return matcher indices */ &kMatcherIndices[58],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7742,10 +8097,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[937],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[826],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7754,10 +8109,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[621],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[633],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7766,10 +8121,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[928],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[825],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7778,10 +8133,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[625],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[637],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7790,10 +8145,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[912],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[824],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7802,10 +8157,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[911],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[823],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7814,10 +8169,10 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[631],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[643],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7826,10 +8181,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[910],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[822],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7838,10 +8193,10 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[635],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[647],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7850,10 +8205,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[909],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[821],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7862,10 +8217,10 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[639],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[651],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7874,10 +8229,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[908],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[820],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7886,10 +8241,10 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[643],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[655],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7898,10 +8253,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[907],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[819],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7910,10 +8265,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[906],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[818],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7922,10 +8277,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[905],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[817],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7934,10 +8289,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[904],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[816],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7946,10 +8301,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[835],
-    /* return matcher indices */ &kMatcherIndices[125],
+    /* parameters */ &kParameters[815],
+    /* return matcher indices */ &kMatcherIndices[58],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -7958,263 +8313,263 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[902],
-    /* return matcher indices */ &kMatcherIndices[111],
+    /* parameters */ &kParameters[814],
+    /* return matcher indices */ &kMatcherIndices[122],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [27] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[516],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [28] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[513],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[955],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [29] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[363],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[956],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [30] */
     /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[311],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[439],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [31] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[220],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[486],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [32] */
     /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[498],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[474],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [33] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[343],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[471],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [34] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[492],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[767],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [35] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[339],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[773],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [36] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[486],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[775],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [37] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[331],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[964],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [38] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[323],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[965],
+    /* return matcher indices */ &kMatcherIndices[120],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [39] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[6],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[285],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[966],
+    /* return matcher indices */ &kMatcherIndices[136],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [40] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[8],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[474],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[967],
+    /* return matcher indices */ &kMatcherIndices[142],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [41] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[319],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[968],
+    /* return matcher indices */ &kMatcherIndices[144],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [42] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[335],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[504],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [43] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[240],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[501],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [44] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[245],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[315],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [45] */
-    /* num parameters */ 6,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[114],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[295],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [46] */
-    /* num parameters */ 4,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[307],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[210],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [47] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[205],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[495],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8222,23 +8577,23 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[299],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[319],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [49] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[235],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[492],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8246,274 +8601,274 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[295],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[323],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [51] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[225],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[489],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [52] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[280],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[335],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [53] */
-    /* num parameters */ 6,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[132],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[347],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [54] */
-    /* num parameters */ 4,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[303],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[180],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [55] */
-    /* num parameters */ 5,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[265],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[468],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [56] */
-    /* num parameters */ 3,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[471],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[355],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [57] */
-    /* num parameters */ 0,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[299],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [58] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[944],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[220],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [59] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[945],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[245],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [60] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[395],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[72],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [61] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[561],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[375],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [62] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[585],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[150],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [63] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[573],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[383],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [64] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[611],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[170],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [65] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[607],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[391],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [66] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[603],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[215],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [67] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[953],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[270],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [68] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[4],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[954],
-    /* return matcher indices */ &kMatcherIndices[57],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[90],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [69] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[6],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[955],
-    /* return matcher indices */ &kMatcherIndices[61],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[407],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [70] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[956],
-    /* return matcher indices */ &kMatcherIndices[65],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[280],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [71] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
+    /* num parameters */ 3,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[443],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[537],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [72] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[150],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[367],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8522,70 +8877,70 @@
     /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[255],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[290],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [74] */
-    /* num parameters */ 6,
+    /* num parameters */ 5,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[90],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[265],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [75] */
-    /* num parameters */ 4,
+    /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[419],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[120],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [76] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[210],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[351],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [77] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 5,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[567],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[185],
+    /* return matcher indices */ &kMatcherIndices[4],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [78] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[411],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[534],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8594,58 +8949,58 @@
     /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[399],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[343],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [80] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[155],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[339],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [81] */
-    /* num parameters */ 3,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[555],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[190],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [82] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[391],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[507],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [83] */
-    /* num parameters */ 3,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[477],
-    /* return matcher indices */ nullptr,
+    /* parameters */ &kParameters[331],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8654,33 +9009,33 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[480],
+    /* parameters */ &kParameters[540],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [85] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[327],
+    /* parameters */ &kParameters[549],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [86] */
-    /* num parameters */ 3,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[483],
+    /* parameters */ &kParameters[415],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8690,9 +9045,9 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[489],
+    /* parameters */ &kParameters[552],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8702,33 +9057,33 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[495],
+    /* parameters */ &kParameters[453],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [89] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[347],
+    /* parameters */ &kParameters[555],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [90] */
-    /* num parameters */ 3,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[501],
+    /* parameters */ &kParameters[419],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8738,9 +9093,9 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[504],
+    /* parameters */ &kParameters[558],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -8750,58 +9105,58 @@
     /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[510],
+    /* parameters */ &kParameters[561],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [93] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[367],
+    /* parameters */ &kParameters[564],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [94] */
-    /* num parameters */ 3,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[519],
+    /* parameters */ &kParameters[431],
     /* return matcher indices */ nullptr,
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [95] */
-    /* num parameters */ 0,
-    /* num template types */ 1,
+    /* num parameters */ 3,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[117],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[573],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [96] */
-    /* num parameters */ 1,
+    /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[934],
-    /* return matcher indices */ &kMatcherIndices[117],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[69],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8810,34 +9165,34 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[935],
-    /* return matcher indices */ &kMatcherIndices[117],
+    /* parameters */ &kParameters[944],
+    /* return matcher indices */ &kMatcherIndices[69],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [98] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[558],
-    /* return matcher indices */ &kMatcherIndices[117],
+    /* parameters */ &kParameters[945],
+    /* return matcher indices */ &kMatcherIndices[69],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [99] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[619],
-    /* return matcher indices */ &kMatcherIndices[117],
+    /* parameters */ &kParameters[498],
+    /* return matcher indices */ &kMatcherIndices[69],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8846,23 +9201,23 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[615],
-    /* return matcher indices */ &kMatcherIndices[117],
+    /* parameters */ &kParameters[751],
+    /* return matcher indices */ &kMatcherIndices[69],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [101] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[939],
-    /* return matcher indices */ &kMatcherIndices[123],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[755],
+    /* return matcher indices */ &kMatcherIndices[69],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8870,10 +9225,10 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[4],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[940],
-    /* return matcher indices */ &kMatcherIndices[125],
+    /* parameters */ &kParameters[949],
+    /* return matcher indices */ &kMatcherIndices[39],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8882,10 +9237,10 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[6],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[941],
-    /* return matcher indices */ &kMatcherIndices[127],
+    /* parameters */ &kParameters[950],
+    /* return matcher indices */ &kMatcherIndices[65],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8894,35 +9249,35 @@
     /* num parameters */ 1,
     /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[6],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[942],
-    /* return matcher indices */ &kMatcherIndices[129],
+    /* parameters */ &kParameters[951],
+    /* return matcher indices */ &kMatcherIndices[58],
     /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [105] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[8],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[879],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[952],
+    /* return matcher indices */ &kMatcherIndices[54],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [106] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[878],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[953],
+    /* return matcher indices */ &kMatcherIndices[43],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -8930,10 +9285,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[877],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[808],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8942,10 +9297,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[876],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[807],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8954,10 +9309,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[875],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[806],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -8966,34 +9321,34 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[874],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[805],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [111] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[873],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[804],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [112] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[872],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[803],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9002,10 +9357,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[871],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[802],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9014,382 +9369,382 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[870],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[801],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [115] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[531],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[800],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [116] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[534],
-    /* return matcher indices */ &kMatcherIndices[49],
+    /* parameters */ &kParameters[799],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [117] */
-    /* num parameters */ 4,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[375],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[759],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [118] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[588],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[771],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [119] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[537],
-    /* return matcher indices */ &kMatcherIndices[49],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[787],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [120] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[540],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[789],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [121] */
-    /* num parameters */ 4,
+    /* num parameters */ 2,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[379],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[793],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [122] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[543],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[791],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [123] */
     /* num parameters */ 2,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[783],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[1],
+    /* parameters */ &kParameters[591],
+    /* return matcher indices */ &kMatcherIndices[77],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [124] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[725],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[1],
+    /* parameters */ &kParameters[595],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [125] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[723],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* num template types */ 0,
+    /* num template numbers */ 3,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[0],
+    /* parameters */ &kParameters[603],
+    /* return matcher indices */ &kMatcherIndices[23],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [126] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[721],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[579],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [127] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[719],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[582],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [128] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[717],
-    /* return matcher indices */ &kMatcherIndices[11],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 4,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[435],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [129] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[715],
-    /* return matcher indices */ &kMatcherIndices[11],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[585],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [130] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[1],
-    /* parameters */ &kParameters[713],
-    /* return matcher indices */ &kMatcherIndices[76],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[447],
+    /* return matcher indices */ &kMatcherIndices[4],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [131] */
-    /* num parameters */ 2,
+    /* num parameters */ 3,
     /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[1],
-    /* parameters */ &kParameters[711],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[588],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [132] */
-    /* num parameters */ 2,
+    /* num parameters */ 4,
     /* num template types */ 0,
-    /* num template numbers */ 3,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[0],
-    /* parameters */ &kParameters[709],
-    /* return matcher indices */ &kMatcherIndices[27],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[359],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [133] */
-    /* num parameters */ 4,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[315],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[576],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [134] */
-    /* num parameters */ 5,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[260],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[601],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [135] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[250],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[104],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [136] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[16],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[126],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[935],
+    /* return matcher indices */ &kMatcherIndices[104],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [137] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[355],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[936],
+    /* return matcher indices */ &kMatcherIndices[104],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [138] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[15],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[290],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[745],
+    /* return matcher indices */ &kMatcherIndices[104],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [139] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[359],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[938],
+    /* return matcher indices */ &kMatcherIndices[108],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [140] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[275],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[939],
+    /* return matcher indices */ &kMatcherIndices[124],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [141] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[6],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[185],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[940],
+    /* return matcher indices */ &kMatcherIndices[122],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [142] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[8],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[138],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[941],
+    /* return matcher indices */ &kMatcherIndices[118],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [143] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 2,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[0],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[84],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[942],
+    /* return matcher indices */ &kMatcherIndices[110],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [144] */
-    /* num parameters */ 7,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[65],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[255],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [145] */
-    /* num parameters */ 5,
+    /* num parameters */ 6,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[190],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[84],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9398,155 +9753,155 @@
     /* num parameters */ 6,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[78],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[108],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [147] */
-    /* num parameters */ 5,
+    /* num parameters */ 7,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[230],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[65],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [148] */
-    /* num parameters */ 6,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[102],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[275],
+    /* return matcher indices */ &kMatcherIndices[106],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [149] */
-    /* num parameters */ 0,
-    /* num template types */ 1,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[114],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [150] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[926],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[285],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [151] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[927],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[144],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [152] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[12],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[623],
-    /* return matcher indices */ &kMatcherIndices[105],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[363],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [153] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[2],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[929],
-    /* return matcher indices */ &kMatcherIndices[109],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[165],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [154] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[4],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[930],
-    /* return matcher indices */ &kMatcherIndices[111],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[160],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [155] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[6],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[931],
-    /* return matcher indices */ &kMatcherIndices[113],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[138],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [156] */
-    /* num parameters */ 1,
-    /* num template types */ 2,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[0],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[932],
-    /* return matcher indices */ &kMatcherIndices[115],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[379],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [157] */
-    /* num parameters */ 4,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[387],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[155],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [158] */
-    /* num parameters */ 5,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[175],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[387],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -9554,202 +9909,202 @@
     /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[180],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[175],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [160] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
+    /* num parameters */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[144],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [161] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[371],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[925],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [162] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[200],
-    /* return matcher indices */ &kMatcherIndices[42],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[922],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [163] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 16,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[431],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[0],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [164] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 4,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[270],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[411],
+    /* return matcher indices */ &kMatcherIndices[212],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [165] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[215],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[831],
+    /* return matcher indices */ &kMatcherIndices[214],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [166] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[120],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[913],
+    /* return matcher indices */ &kMatcherIndices[154],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [167] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[415],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[186],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [168] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[195],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[1011],
+    /* return matcher indices */ &kMatcherIndices[186],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [169] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[407],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1004],
+    /* return matcher indices */ &kMatcherIndices[186],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [170] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 8,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[165],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[57],
+    /* return matcher indices */ &kMatcherIndices[186],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [171] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 4,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[160],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[399],
+    /* return matcher indices */ &kMatcherIndices[186],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [172] */
-    /* num parameters */ 6,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[72],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[983],
+    /* return matcher indices */ &kMatcherIndices[190],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [173] */
-    /* num parameters */ 4,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[383],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[976],
+    /* return matcher indices */ &kMatcherIndices[188],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [174] */
-    /* num parameters */ 5,
-    /* num template types */ 0,
+    /* num parameters */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[170],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[160],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [175] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[169],
+    /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9758,70 +10113,70 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[962],
-    /* return matcher indices */ &kMatcherIndices[169],
+    /* parameters */ &kParameters[985],
+    /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [177] */
-    /* num parameters */ 1,
+    /* num parameters */ 8,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[957],
-    /* return matcher indices */ &kMatcherIndices[171],
+    /* parameters */ &kParameters[49],
+    /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [178] */
-    /* num parameters */ 8,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[49],
-    /* return matcher indices */ &kMatcherIndices[171],
+    /* parameters */ &kParameters[785],
+    /* return matcher indices */ &kMatcherIndices[160],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [179] */
-    /* num parameters */ 4,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[427],
-    /* return matcher indices */ &kMatcherIndices[171],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[988],
+    /* return matcher indices */ &kMatcherIndices[166],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [180] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[149],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[989],
+    /* return matcher indices */ &kMatcherIndices[164],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [181] */
-    /* num parameters */ 1,
+    /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[963],
-    /* return matcher indices */ &kMatcherIndices[149],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[146],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9830,47 +10185,47 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[964],
-    /* return matcher indices */ &kMatcherIndices[151],
+    /* parameters */ &kParameters[970],
+    /* return matcher indices */ &kMatcherIndices[146],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [183] */
-    /* num parameters */ 6,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[108],
-    /* return matcher indices */ &kMatcherIndices[151],
+    /* parameters */ &kParameters[971],
+    /* return matcher indices */ &kMatcherIndices[146],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [184] */
-    /* num parameters */ 2,
+    /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[595],
-    /* return matcher indices */ &kMatcherIndices[151],
+    /* parameters */ &kParameters[371],
+    /* return matcher indices */ &kMatcherIndices[146],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [185] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[884],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[777],
+    /* return matcher indices */ &kMatcherIndices[146],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -9878,118 +10233,118 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[883],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[974],
+    /* return matcher indices */ &kMatcherIndices[150],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [187] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[882],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[975],
+    /* return matcher indices */ &kMatcherIndices[148],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [188] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[881],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[174],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [189] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[3],
-    /* parameters */ &kParameters[880],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[998],
+    /* return matcher indices */ &kMatcherIndices[174],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [190] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[4],
+    /* parameters */ &kParameters[999],
+    /* return matcher indices */ &kMatcherIndices[174],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [191] */
-    /* num parameters */ 1,
+    /* num parameters */ 9,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[958],
-    /* return matcher indices */ &kMatcherIndices[4],
+    /* parameters */ &kParameters[40],
+    /* return matcher indices */ &kMatcherIndices[174],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [192] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[959],
-    /* return matcher indices */ &kMatcherIndices[147],
+    /* parameters */ &kParameters[456],
+    /* return matcher indices */ &kMatcherIndices[174],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [193] */
-    /* num parameters */ 4,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[423],
-    /* return matcher indices */ &kMatcherIndices[147],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1002],
+    /* return matcher indices */ &kMatcherIndices[178],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [194] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[599],
-    /* return matcher indices */ &kMatcherIndices[147],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1003],
+    /* return matcher indices */ &kMatcherIndices[176],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [195] */
     /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[161],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -9998,10 +10353,10 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[978],
-    /* return matcher indices */ &kMatcherIndices[161],
+    /* parameters */ &kParameters[991],
+    /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10010,22 +10365,22 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[979],
-    /* return matcher indices */ &kMatcherIndices[163],
+    /* parameters */ &kParameters[992],
+    /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [198] */
-    /* num parameters */ 9,
+    /* num parameters */ 6,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[40],
-    /* return matcher indices */ &kMatcherIndices[163],
+    /* parameters */ &kParameters[132],
+    /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10034,23 +10389,23 @@
     /* num parameters */ 3,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[579],
-    /* return matcher indices */ &kMatcherIndices[163],
+    /* parameters */ &kParameters[459],
+    /* return matcher indices */ &kMatcherIndices[168],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [200] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[157],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[995],
+    /* return matcher indices */ &kMatcherIndices[172],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10058,70 +10413,70 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[973],
-    /* return matcher indices */ &kMatcherIndices[157],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[996],
+    /* return matcher indices */ &kMatcherIndices[170],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [202] */
-    /* num parameters */ 1,
+    /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[974],
-    /* return matcher indices */ &kMatcherIndices[159],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [203] */
-    /* num parameters */ 6,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[96],
-    /* return matcher indices */ &kMatcherIndices[159],
+    /* parameters */ &kParameters[977],
+    /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [204] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[12],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[576],
-    /* return matcher indices */ &kMatcherIndices[159],
+    /* parameters */ &kParameters[978],
+    /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [205] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num parameters */ 6,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[175],
+    /* parameters */ &kParameters[126],
+    /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [206] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[925],
-    /* return matcher indices */ &kMatcherIndices[175],
+    /* parameters */ &kParameters[783],
+    /* return matcher indices */ &kMatcherIndices[152],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10130,166 +10485,166 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[922],
-    /* return matcher indices */ &kMatcherIndices[177],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[981],
+    /* return matcher indices */ &kMatcherIndices[158],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [208] */
-    /* num parameters */ 12,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[28],
-    /* return matcher indices */ &kMatcherIndices[177],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[982],
+    /* return matcher indices */ &kMatcherIndices[156],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [209] */
-    /* num parameters */ 4,
+    /* num parameters */ 0,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[435],
-    /* return matcher indices */ &kMatcherIndices[177],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[196],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [210] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[13],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[737],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[969],
+    /* return matcher indices */ &kMatcherIndices[196],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [211] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[735],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[954],
+    /* return matcher indices */ &kMatcherIndices[196],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [212] */
-    /* num parameters */ 2,
+    /* num parameters */ 12,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[731],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[28],
+    /* return matcher indices */ &kMatcherIndices[196],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [213] */
-    /* num parameters */ 2,
+    /* num parameters */ 4,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[729],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[403],
+    /* return matcher indices */ &kMatcherIndices[196],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [214] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[727],
-    /* return matcher indices */ &kMatcherIndices[11],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[4],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[931],
+    /* return matcher indices */ &kMatcherIndices[210],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [215] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[751],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[928],
+    /* return matcher indices */ &kMatcherIndices[208],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [216] */
-    /* num parameters */ 2,
+    /* num parameters */ 0,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[749],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[180],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [217] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[747],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[13],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1005],
+    /* return matcher indices */ &kMatcherIndices[180],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [218] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[745],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[12],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1006],
+    /* return matcher indices */ &kMatcherIndices[180],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [219] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[743],
-    /* return matcher indices */ &kMatcherIndices[11],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 12,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[11],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[16],
+    /* return matcher indices */ &kMatcherIndices[180],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [220] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[11],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[189],
+    /* parameters */ &kParameters[450],
+    /* return matcher indices */ &kMatcherIndices[180],
     /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10298,11 +10653,11 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[4],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[913],
-    /* return matcher indices */ &kMatcherIndices[189],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1009],
+    /* return matcher indices */ &kMatcherIndices[184],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10310,238 +10665,238 @@
     /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[2],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[895],
-    /* return matcher indices */ &kMatcherIndices[191],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[914],
+    /* return matcher indices */ &kMatcherIndices[182],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [223] */
-    /* num parameters */ 16,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[0],
-    /* return matcher indices */ &kMatcherIndices[191],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[395],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [224] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[351],
-    /* return matcher indices */ &kMatcherIndices[191],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[240],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [225] */
-    /* num parameters */ 0,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[153],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[260],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [226] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[968],
-    /* return matcher indices */ &kMatcherIndices[153],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[78],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [227] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[969],
-    /* return matcher indices */ &kMatcherIndices[155],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[443],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [228] */
-    /* num parameters */ 8,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[57],
-    /* return matcher indices */ &kMatcherIndices[155],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[195],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [229] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[593],
-    /* return matcher indices */ &kMatcherIndices[155],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[307],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [230] */
-    /* num parameters */ 0,
+    /* num parameters */ 5,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[165],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[230],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [231] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[9],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[982],
-    /* return matcher indices */ &kMatcherIndices[165],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[235],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [232] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num parameters */ 6,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[977],
-    /* return matcher indices */ &kMatcherIndices[167],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[96],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [233] */
-    /* num parameters */ 12,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[16],
-    /* return matcher indices */ &kMatcherIndices[167],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[303],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [234] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[10],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[582],
-    /* return matcher indices */ &kMatcherIndices[167],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[250],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [235] */
-    /* num parameters */ 2,
+    /* num parameters */ 4,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[689],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[327],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [236] */
-    /* num parameters */ 2,
+    /* num parameters */ 5,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[687],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[200],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [237] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 5,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[685],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[205],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [238] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[683],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 6,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[102],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [239] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 4,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[699],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[311],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [240] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[697],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 5,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[225],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [241] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[695],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[741],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10550,10 +10905,10 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[693],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[747],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10561,11 +10916,11 @@
     /* [243] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[707],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[749],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -10574,347 +10929,347 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[705],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[753],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [245] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[703],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[757],
+    /* return matcher indices */ &kMatcherIndices[11],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [246] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[701],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[813],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [247] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[681],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[812],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [248] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[679],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[811],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [249] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[677],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[810],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [250] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[675],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[3],
+    /* parameters */ &kParameters[809],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [251] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[522],
+    /* parameters */ &kParameters[663],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [252] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[525],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[679],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [253] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[528],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[683],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [254] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[689],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [255] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[914],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[739],
+    /* return matcher indices */ &kMatcherIndices[11],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [256] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[18],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[915],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[681],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [257] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[649],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [258] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[917],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[653],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [259] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[17],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[918],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[657],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [260] */
-    /* num parameters */ 0,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[667],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [261] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[920],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[669],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [262] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[16],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[921],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[671],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [263] */
-    /* num parameters */ 0,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[673],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [264] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[923],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[675],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [265] */
-    /* num parameters */ 1,
-    /* num template types */ 1,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[15],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[924],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[677],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [266] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[459],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[691],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [267] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[462],
+    /* parameters */ &kParameters[693],
     /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [268] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[465],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[611],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [269] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[852],
-    /* return matcher indices */ &kMatcherIndices[21],
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[623],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [270] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[853],
-    /* return matcher indices */ &kMatcherIndices[44],
+    /* parameters */ &kParameters[639],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [271] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[810],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[641],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [272] */
-    /* num parameters */ 1,
+    /* num parameters */ 0,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[824],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -10922,263 +11277,263 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[827],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[920],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [274] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[828],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[22],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[921],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [275] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
+    /* num parameters */ 0,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[439],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [276] */
-    /* num parameters */ 4,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[403],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[932],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [277] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[19],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[832],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[933],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [278] */
-    /* num parameters */ 1,
+    /* num parameters */ 0,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[840],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[66],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [279] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[781],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[929],
+    /* return matcher indices */ &kMatcherIndices[66],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [280] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[779],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[18],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[930],
+    /* return matcher indices */ &kMatcherIndices[66],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [281] */
-    /* num parameters */ 1,
+    /* num parameters */ 0,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[842],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [282] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[843],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[926],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [283] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
     /* template types */ &kTemplateTypes[20],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[844],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[927],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [284] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[845],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[18],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[510],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [285] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[846],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[18],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[543],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [286] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[18],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[847],
+    /* parameters */ &kParameters[546],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [287] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 0,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[765],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [288] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[763],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[923],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConstructor, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [289] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[21],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[761],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[924],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsConverter, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [290] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[759],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[477],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [291] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[799],
-    /* return matcher indices */ &kMatcherIndices[202],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[480],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [292] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[798],
-    /* return matcher indices */ &kMatcherIndices[145],
+    /* parameters */ &kParameters[483],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [293] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[848],
-    /* return matcher indices */ &kMatcherIndices[201],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[860],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [294] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[849],
-    /* return matcher indices */ &kMatcherIndices[119],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[861],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11186,10 +11541,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[801],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[890],
+    /* return matcher indices */ &kMatcherIndices[225],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11198,10 +11553,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[800],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[889],
+    /* return matcher indices */ &kMatcherIndices[162],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11210,10 +11565,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[825],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[888],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
@@ -11222,106 +11577,106 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[826],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[887],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [299] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[450],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[892],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [300] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[447],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[891],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [301] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[805],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[884],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [302] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[804],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[883],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [303] */
-    /* num parameters */ 1,
+    /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[807],
+    /* parameters */ &kParameters[423],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [304] */
-    /* num parameters */ 1,
+    /* num parameters */ 4,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[806],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[427],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [305] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[468],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[882],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [306] */
-    /* num parameters */ 3,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[456],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[881],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11330,10 +11685,10 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[741],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[593],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11342,10 +11697,10 @@
     /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[739],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[631],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11354,10 +11709,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[863],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[880],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11366,10 +11721,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[864],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[879],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11378,10 +11733,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[809],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[878],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11390,10 +11745,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[808],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[877],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11402,10 +11757,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[811],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[876],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11414,83 +11769,83 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[886],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[875],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [315] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[865],
+    /* parameters */ &kParameters[769],
     /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [316] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[866],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[765],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [317] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[867],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[763],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [318] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[885],
+    /* parameters */ &kParameters[761],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [319] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[813],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[522],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [320] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[812],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[528],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11498,10 +11853,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[887],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[874],
+    /* return matcher indices */ &kMatcherIndices[224],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11510,10 +11865,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[888],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[873],
+    /* return matcher indices */ &kMatcherIndices[114],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11522,10 +11877,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[889],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[894],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11534,10 +11889,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[890],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[893],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11546,11 +11901,11 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[891],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[886],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11558,82 +11913,82 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[892],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[885],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [327] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[549],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[898],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [328] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[552],
+    /* parameters */ &kParameters[897],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [329] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[893],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[513],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [330] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[894],
+    /* parameters */ &kParameters[516],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [331] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[673],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[899],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [332] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[671],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[1012],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11642,11 +11997,11 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[815],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[901],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11654,34 +12009,34 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[814],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[900],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [335] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[896],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[687],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [336] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[897],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[685],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11690,10 +12045,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[898],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[866],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -11702,35 +12057,35 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[899],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[865],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [339] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[629],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[903],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [340] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[627],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[902],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11738,11 +12093,11 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[901],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[905],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11750,179 +12105,179 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[903],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[904],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [343] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[817],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[864],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [344] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[816],
+    /* parameters */ &kParameters[857],
     /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [345] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[645],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[856],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [346] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[641],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[855],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [347] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[649],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[907],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [348] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[647],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[906],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [349] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[653],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[853],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [350] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[651],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[852],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [351] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[657],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[851],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [352] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[655],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[850],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [353] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[661],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[849],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [354] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[659],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[848],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [355] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[819],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[567],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [356] */
-    /* num parameters */ 1,
+    /* num parameters */ 3,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[818],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[570],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11930,11 +12285,11 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[821],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[846],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -11942,94 +12297,94 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[820],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[845],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [359] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[823],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[599],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [360] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[822],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
+    /* parameters */ &kParameters[597],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [361] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[787],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[896],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [362] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
+    /* num parameters */ 1,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[789],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[895],
+    /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [363] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[983],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[843],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [364] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[960],
-    /* return matcher indices */ &kMatcherIndices[37],
+    /* parameters */ &kParameters[842],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [365] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[869],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* parameters */ &kParameters[841],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12037,11 +12392,11 @@
     /* [366] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[868],
-    /* return matcher indices */ &kMatcherIndices[41],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[840],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12050,11 +12405,11 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[831],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[909],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12062,273 +12417,273 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[830],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[908],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [369] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[665],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[838],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [370] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[13],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[663],
-    /* return matcher indices */ &kMatcherIndices[44],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[837],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [371] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[834],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[911],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [372] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[833],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[910],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [373] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[837],
+    /* parameters */ &kParameters[735],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [374] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[836],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[743],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [375] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[839],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[1010],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [376] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[838],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[912],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline),
     /* const eval */ nullptr,
   },
   {
     /* [377] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[936],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[795],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [378] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[851],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[719],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [379] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[947],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[715],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [380] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[946],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[717],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [381] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[570],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[711],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [382] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[564],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[713],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [383] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[949],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[729],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [384] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[948],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[727],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [385] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[597],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[917],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [386] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[601],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[916],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [387] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[691],
+    /* parameters */ &kParameters[854],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [388] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[757],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[844],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [389] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[975],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[937],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [390] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[970],
+    /* parameters */ &kParameters[918],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12336,23 +12691,23 @@
   {
     /* [391] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[980],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[947],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [392] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[976],
+    /* parameters */ &kParameters[946],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
@@ -12360,12 +12715,12 @@
   {
     /* [393] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[967],
-    /* return matcher indices */ &kMatcherIndices[21],
+    /* parameters */ &kParameters[798],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12373,156 +12728,156 @@
     /* [394] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[972],
-    /* return matcher indices */ &kMatcherIndices[21],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[797],
+    /* return matcher indices */ &kMatcherIndices[38],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [395] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[933],
-    /* return matcher indices */ &kMatcherIndices[21],
+    /* parameters */ &kParameters[957],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [396] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[943],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [397] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[916],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [398] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[919],
+    /* parameters */ &kParameters[948],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [399] */
-    /* num parameters */ 1,
+    /* [397] */
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[19],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[856],
-    /* return matcher indices */ &kMatcherIndices[1],
+    /* parameters */ &kParameters[707],
+    /* return matcher indices */ &kMatcherIndices[17],
     /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
+    /* [398] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[709],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [399] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[959],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
     /* [400] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[19],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[857],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[958],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [401] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[854],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[961],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [402] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[855],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[960],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [403] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[803],
-    /* return matcher indices */ &kMatcherIndices[5],
+    /* parameters */ &kParameters[462],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [404] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num parameters */ 3,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[14],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[802],
+    /* parameters */ &kParameters[465],
     /* return matcher indices */ &kMatcherIndices[31],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [405] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[637],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[963],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [406] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[633],
-    /* return matcher indices */ &kMatcherIndices[37],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[962],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12530,59 +12885,59 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[971],
-    /* return matcher indices */ &kMatcherIndices[109],
+    /* parameters */ &kParameters[973],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [408] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[755],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[972],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [409] */
     /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[591],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[781],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [410] */
     /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[767],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[779],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [411] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[769],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[16],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[703],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12590,23 +12945,23 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[771],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[16],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[705],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [413] */
     /* num parameters */ 2,
     /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[773],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[16],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[699],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12614,83 +12969,83 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[775],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[16],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[701],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [415] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[777],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[980],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [416] */
     /* num parameters */ 1,
-    /* num template types */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[841],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [417] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[669],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [418] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[667],
-    /* return matcher indices */ &kMatcherIndices[21],
-    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
-    /* const eval */ nullptr,
-  },
-  {
-    /* [419] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[860],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[979],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
-    /* [420] */
-    /* num parameters */ 0,
+    /* [417] */
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[987],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [418] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[986],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [419] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[659],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [420] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[661],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12698,10 +13053,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[961],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* parameters */ &kParameters[994],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12709,11 +13064,11 @@
     /* [422] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[965],
-    /* return matcher indices */ &kMatcherIndices[42],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[993],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12722,23 +13077,23 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[966],
-    /* return matcher indices */ &kMatcherIndices[109],
+    /* parameters */ &kParameters[1007],
+    /* return matcher indices */ &kMatcherIndices[17],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [424] */
-    /* num parameters */ 2,
-    /* num template types */ 1,
+    /* num parameters */ 1,
+    /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
-    /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[753],
-    /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[1001],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12746,10 +13101,10 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[797],
-    /* return matcher indices */ &kMatcherIndices[109],
+    /* parameters */ &kParameters[997],
+    /* return matcher indices */ &kMatcherIndices[17],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12757,47 +13112,47 @@
     /* [426] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 2,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[5],
-    /* parameters */ &kParameters[900],
-    /* return matcher indices */ &kMatcherIndices[23],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[1008],
+    /* return matcher indices */ &kMatcherIndices[17],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [427] */
-    /* num parameters */ 0,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[984],
-    /* return matcher indices */ nullptr,
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[943],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [428] */
-    /* num parameters */ 3,
+    /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[507],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* parameters */ &kParameters[990],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [429] */
-    /* num parameters */ 2,
+    /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[733],
-    /* return matcher indices */ &kMatcherIndices[31],
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[919],
+    /* return matcher indices */ &kMatcherIndices[14],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
@@ -12805,36 +13160,36 @@
     /* [430] */
     /* num parameters */ 1,
     /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[862],
-    /* return matcher indices */ &kMatcherIndices[62],
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[934],
+    /* return matcher indices */ &kMatcherIndices[34],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [431] */
     /* num parameters */ 1,
-    /* num template types */ 0,
+    /* num template types */ 1,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[23],
     /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[861],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[862],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [432] */
     /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[859],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[23],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[863],
+    /* return matcher indices */ &kMatcherIndices[31],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12842,11 +13197,11 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[10],
     /* parameters */ &kParameters[858],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12854,59 +13209,59 @@
     /* num parameters */ 1,
     /* num template types */ 0,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[24],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[850],
-    /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* parameters */ &kParameters[859],
+    /* return matcher indices */ &kMatcherIndices[45],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [435] */
-    /* num parameters */ 1,
-    /* num template types */ 0,
-    /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[829],
-    /* return matcher indices */ &kMatcherIndices[5],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[731],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [436] */
-    /* num parameters */ 3,
-    /* num template types */ 0,
+    /* num parameters */ 2,
+    /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[20],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[453],
+    /* parameters */ &kParameters[733],
     /* return matcher indices */ &kMatcherIndices[31],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [437] */
-    /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[795],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num parameters */ 3,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[531],
+    /* return matcher indices */ &kMatcherIndices[200],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [438] */
     /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[793],
-    /* return matcher indices */ &kMatcherIndices[41],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[645],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
@@ -12914,49 +13269,409 @@
     /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[8],
-    /* template numbers */ &kTemplateNumbers[6],
-    /* parameters */ &kParameters[791],
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[635],
     /* return matcher indices */ &kMatcherIndices[1],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [440] */
     /* num parameters */ 2,
-    /* num template types */ 0,
-    /* num template numbers */ 0,
-    /* template types */ &kTemplateTypes[20],
-    /* template numbers */ &kTemplateNumbers[10],
-    /* parameters */ &kParameters[785],
-    /* return matcher indices */ &kMatcherIndices[123],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[615],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [441] */
-    /* num parameters */ 1,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[14],
-    /* template numbers */ &kTemplateNumbers[8],
-    /* parameters */ &kParameters[981],
-    /* return matcher indices */ &kMatcherIndices[62],
-    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[627],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
   {
     /* [442] */
-    /* num parameters */ 3,
+    /* num parameters */ 2,
     /* num template types */ 1,
     /* num template numbers */ 1,
-    /* template types */ &kTemplateTypes[11],
+    /* template types */ &kTemplateTypes[10],
     /* template numbers */ &kTemplateNumbers[9],
-    /* parameters */ &kParameters[546],
-    /* return matcher indices */ &kMatcherIndices[173],
+    /* parameters */ &kParameters[619],
+    /* return matcher indices */ &kMatcherIndices[1],
     /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
     /* const eval */ nullptr,
   },
+  {
+    /* [443] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[613],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [444] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[609],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [445] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[607],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [446] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[695],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [447] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[697],
+    /* return matcher indices */ &kMatcherIndices[17],
+    /* flags */ OverloadFlags(OverloadFlag::kIsOperator, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [448] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[605],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [449] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[10],
+    /* template numbers */ &kTemplateNumbers[9],
+    /* parameters */ &kParameters[847],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [450] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [451] */
+    /* num parameters */ 0,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[1013],
+    /* return matcher indices */ nullptr,
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [452] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[832],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [453] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[833],
+    /* return matcher indices */ &kMatcherIndices[106],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [454] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[834],
+    /* return matcher indices */ &kMatcherIndices[108],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [455] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[835],
+    /* return matcher indices */ &kMatcherIndices[108],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [456] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[836],
+    /* return matcher indices */ &kMatcherIndices[108],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [457] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 2,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[5],
+    /* parameters */ &kParameters[839],
+    /* return matcher indices */ &kMatcherIndices[19],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [458] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[525],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [459] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[665],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [460] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[867],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [461] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[868],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [462] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[869],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [463] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[870],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [464] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[872],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [465] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[737],
+    /* return matcher indices */ &kMatcherIndices[39],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [466] */
+    /* num parameters */ 3,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[519],
+    /* return matcher indices */ &kMatcherIndices[34],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [467] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[721],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [468] */
+    /* num parameters */ 2,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[723],
+    /* return matcher indices */ &kMatcherIndices[38],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [469] */
+    /* num parameters */ 2,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[14],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[725],
+    /* return matcher indices */ &kMatcherIndices[1],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [470] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[6],
+    /* parameters */ &kParameters[915],
+    /* return matcher indices */ &kMatcherIndices[14],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [471] */
+    /* num parameters */ 1,
+    /* num template types */ 1,
+    /* num template numbers */ 1,
+    /* template types */ &kTemplateTypes[17],
+    /* template numbers */ &kTemplateNumbers[8],
+    /* parameters */ &kParameters[1000],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
+  {
+    /* [472] */
+    /* num parameters */ 1,
+    /* num template types */ 0,
+    /* num template numbers */ 0,
+    /* template types */ &kTemplateTypes[24],
+    /* template numbers */ &kTemplateNumbers[10],
+    /* parameters */ &kParameters[871],
+    /* return matcher indices */ &kMatcherIndices[55],
+    /* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
+    /* const eval */ nullptr,
+  },
 };
 
 constexpr IntrinsicInfo kBuiltins[] = {
@@ -12965,549 +13680,570 @@
     /* fn abs<T : fiu32>(T) -> T */
     /* fn abs<N : num, T : fiu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[363],
+    /* overloads */ &kOverloads[387],
   },
   {
     /* [1] */
     /* fn acos(f32) -> f32 */
     /* fn acos<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[397],
+    /* overloads */ &kOverloads[429],
   },
   {
     /* [2] */
-    /* fn all(bool) -> bool */
-    /* fn all<N : num>(vec<N, bool>) -> bool */
+    /* fn acosh(f32) -> f32 */
+    /* fn acosh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[395],
+    /* overloads */ &kOverloads[427],
   },
   {
     /* [3] */
-    /* fn any(bool) -> bool */
-    /* fn any<N : num>(vec<N, bool>) -> bool */
+    /* fn all(bool) -> bool */
+    /* fn all<N : num>(vec<N, bool>) -> bool */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[393],
+    /* overloads */ &kOverloads[425],
   },
   {
     /* [4] */
-    /* fn arrayLength<T, A : access>(ptr<storage, array<T>, A>) -> u32 */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[441],
+    /* fn any(bool) -> bool */
+    /* fn any<N : num>(vec<N, bool>) -> bool */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[423],
   },
   {
     /* [5] */
-    /* fn asin(f32) -> f32 */
-    /* fn asin<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[391],
+    /* fn arrayLength<T, A : access>(ptr<storage, array<T>, A>) -> u32 */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[471],
   },
   {
     /* [6] */
-    /* fn atan(f32) -> f32 */
-    /* fn atan<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* fn asin(f32) -> f32 */
+    /* fn asin<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[389],
+    /* overloads */ &kOverloads[421],
   },
   {
     /* [7] */
-    /* fn atan2(f32, f32) -> f32 */
-    /* fn atan2<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* fn asinh(f32) -> f32 */
+    /* fn asinh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[385],
+    /* overloads */ &kOverloads[417],
   },
   {
     /* [8] */
-    /* fn ceil(f32) -> f32 */
-    /* fn ceil<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* fn atan(f32) -> f32 */
+    /* fn atan<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[383],
+    /* overloads */ &kOverloads[415],
   },
   {
     /* [9] */
-    /* fn clamp<T : fiu32>(T, T, T) -> T */
-    /* fn clamp<N : num, T : fiu32>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* fn atan2(f32, f32) -> f32 */
+    /* fn atan2<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[381],
+    /* overloads */ &kOverloads[409],
   },
   {
     /* [10] */
-    /* fn cos(f32) -> f32 */
-    /* fn cos<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* fn atanh(f32) -> f32 */
+    /* fn atanh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[379],
+    /* overloads */ &kOverloads[407],
   },
   {
     /* [11] */
-    /* fn cosh(f32) -> f32 */
-    /* fn cosh<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* fn ceil(f32) -> f32 */
+    /* fn ceil<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[377],
+    /* overloads */ &kOverloads[405],
   },
   {
     /* [12] */
-    /* fn countLeadingZeros<T : iu32>(T) -> T */
-    /* fn countLeadingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[375],
-  },
-  {
-    /* [13] */
-    /* fn countOneBits<T : iu32>(T) -> T */
-    /* fn countOneBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[373],
-  },
-  {
-    /* [14] */
-    /* fn countTrailingZeros<T : iu32>(T) -> T */
-    /* fn countTrailingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[371],
-  },
-  {
-    /* [15] */
-    /* fn cross(vec3<f32>, vec3<f32>) -> vec3<f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[440],
-  },
-  {
-    /* [16] */
-    /* fn degrees(f32) -> f32 */
-    /* fn degrees<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[367],
-  },
-  {
-    /* [17] */
-    /* fn determinant<N : num>(mat<N, N, f32>) -> f32 */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[435],
-  },
-  {
-    /* [18] */
-    /* fn distance(f32, f32) -> f32 */
-    /* fn distance<N : num>(vec<N, f32>, vec<N, f32>) -> f32 */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[361],
-  },
-  {
-    /* [19] */
-    /* fn dot<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> T */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[439],
-  },
-  {
-    /* [20] */
-    /* fn dot4I8Packed(u32, u32) -> i32 */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[438],
-  },
-  {
-    /* [21] */
-    /* fn dot4U8Packed(u32, u32) -> u32 */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[437],
-  },
-  {
-    /* [22] */
-    /* fn dpdx(f32) -> f32 */
-    /* fn dpdx<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[359],
-  },
-  {
-    /* [23] */
-    /* fn dpdxCoarse(f32) -> f32 */
-    /* fn dpdxCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[357],
-  },
-  {
-    /* [24] */
-    /* fn dpdxFine(f32) -> f32 */
-    /* fn dpdxFine<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[355],
-  },
-  {
-    /* [25] */
-    /* fn dpdy(f32) -> f32 */
-    /* fn dpdy<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[343],
-  },
-  {
-    /* [26] */
-    /* fn dpdyCoarse(f32) -> f32 */
-    /* fn dpdyCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[333],
-  },
-  {
-    /* [27] */
-    /* fn dpdyFine(f32) -> f32 */
-    /* fn dpdyFine<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[319],
-  },
-  {
-    /* [28] */
-    /* fn exp(f32) -> f32 */
-    /* fn exp<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[313],
-  },
-  {
-    /* [29] */
-    /* fn exp2(f32) -> f32 */
-    /* fn exp2<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[311],
-  },
-  {
-    /* [30] */
-    /* fn extractBits<T : iu32>(T, u32, u32) -> T */
-    /* fn extractBits<N : num, T : iu32>(vec<N, T>, u32, u32) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[305],
-  },
-  {
-    /* [31] */
-    /* fn faceForward<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[436],
-  },
-  {
-    /* [32] */
-    /* fn firstLeadingBit<T : iu32>(T) -> T */
-    /* fn firstLeadingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[303],
-  },
-  {
-    /* [33] */
-    /* fn firstTrailingBit<T : iu32>(T) -> T */
-    /* fn firstTrailingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[301],
-  },
-  {
-    /* [34] */
-    /* fn floor(f32) -> f32 */
-    /* fn floor<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* fn clamp<T : fiu32>(T, T, T) -> T */
+    /* fn clamp<N : num, T : fiu32>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[403],
   },
   {
-    /* [35] */
-    /* fn fma(f32, f32, f32) -> f32 */
-    /* fn fma<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* [13] */
+    /* fn cos(f32) -> f32 */
+    /* fn cos<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[299],
+    /* overloads */ &kOverloads[401],
   },
   {
-    /* [36] */
-    /* fn fract(f32) -> f32 */
-    /* fn fract<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [14] */
+    /* fn cosh(f32) -> f32 */
+    /* fn cosh<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[295],
+    /* overloads */ &kOverloads[399],
   },
   {
-    /* [37] */
-    /* fn frexp(f32) -> __frexp_result */
-    /* fn frexp<N : num>(vec<N, f32>) -> __frexp_result_vec<N> */
+    /* [15] */
+    /* fn countLeadingZeros<T : iu32>(T) -> T */
+    /* fn countLeadingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[291],
+    /* overloads */ &kOverloads[395],
   },
   {
-    /* [38] */
-    /* fn fwidth(f32) -> f32 */
-    /* fn fwidth<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [16] */
+    /* fn countOneBits<T : iu32>(T) -> T */
+    /* fn countOneBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[271],
+    /* overloads */ &kOverloads[391],
   },
   {
-    /* [39] */
-    /* fn fwidthCoarse(f32) -> f32 */
-    /* fn fwidthCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [17] */
+    /* fn countTrailingZeros<T : iu32>(T) -> T */
+    /* fn countTrailingZeros<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[297],
+    /* overloads */ &kOverloads[389],
   },
   {
-    /* [40] */
-    /* fn fwidthFine(f32) -> f32 */
-    /* fn fwidthFine<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[273],
-  },
-  {
-    /* [41] */
-    /* fn insertBits<T : iu32>(T, T, u32, u32) -> T */
-    /* fn insertBits<N : num, T : iu32>(vec<N, T>, vec<N, T>, u32, u32) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[275],
-  },
-  {
-    /* [42] */
-    /* fn inverseSqrt(f32) -> f32 */
-    /* fn inverseSqrt<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[277],
-  },
-  {
-    /* [43] */
-    /* fn ldexp(f32, i32) -> f32 */
-    /* fn ldexp<N : num>(vec<N, f32>, vec<N, i32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[279],
-  },
-  {
-    /* [44] */
-    /* fn length(f32) -> f32 */
-    /* fn length<N : num>(vec<N, f32>) -> f32 */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[281],
-  },
-  {
-    /* [45] */
-    /* fn log(f32) -> f32 */
-    /* fn log<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[283],
-  },
-  {
-    /* [46] */
-    /* fn log2(f32) -> f32 */
-    /* fn log2<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[285],
-  },
-  {
-    /* [47] */
-    /* fn max<T : fiu32>(T, T) -> T */
-    /* fn max<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[287],
-  },
-  {
-    /* [48] */
-    /* fn min<T : fiu32>(T, T) -> T */
-    /* fn min<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> vec<N, T> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[289],
-  },
-  {
-    /* [49] */
-    /* fn mix(f32, f32, f32) -> f32 */
-    /* fn mix<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
-    /* fn mix<N : num>(vec<N, f32>, vec<N, f32>, f32) -> vec<N, f32> */
-    /* num overloads */ 3,
-    /* overloads */ &kOverloads[266],
-  },
-  {
-    /* [50] */
-    /* fn modf(f32) -> __modf_result */
-    /* fn modf<N : num>(vec<N, f32>) -> __modf_result_vec<N> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[293],
-  },
-  {
-    /* [51] */
-    /* fn normalize<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [18] */
+    /* fn cross(vec3<f32>, vec3<f32>) -> vec3<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[434],
+    /* overloads */ &kOverloads[465],
   },
   {
-    /* [52] */
-    /* fn pack2x16float(vec2<f32>) -> u32 */
+    /* [19] */
+    /* fn degrees(f32) -> f32 */
+    /* fn degrees<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[385],
+  },
+  {
+    /* [20] */
+    /* fn determinant<N : num>(mat<N, N, f32>) -> f32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[433],
+    /* overloads */ &kOverloads[470],
   },
   {
-    /* [53] */
-    /* fn pack2x16snorm(vec2<f32>) -> u32 */
+    /* [21] */
+    /* fn distance(f32, f32) -> f32 */
+    /* fn distance<N : num>(vec<N, f32>, vec<N, f32>) -> f32 */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[383],
+  },
+  {
+    /* [22] */
+    /* fn dot<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[432],
+    /* overloads */ &kOverloads[469],
   },
   {
-    /* [54] */
-    /* fn pack2x16unorm(vec2<f32>) -> u32 */
+    /* [23] */
+    /* fn dot4I8Packed(u32, u32) -> i32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[419],
+    /* overloads */ &kOverloads[468],
   },
   {
-    /* [55] */
-    /* fn pack4x8snorm(vec4<f32>) -> u32 */
+    /* [24] */
+    /* fn dot4U8Packed(u32, u32) -> u32 */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[431],
+    /* overloads */ &kOverloads[467],
   },
   {
-    /* [56] */
-    /* fn pack4x8unorm(vec4<f32>) -> u32 */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[430],
-  },
-  {
-    /* [57] */
-    /* fn pow(f32, f32) -> f32 */
-    /* fn pow<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* [25] */
+    /* fn dpdx(f32) -> f32 */
+    /* fn dpdx<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[307],
+    /* overloads */ &kOverloads[375],
   },
   {
-    /* [58] */
-    /* fn radians(f32) -> f32 */
-    /* fn radians<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [26] */
+    /* fn dpdxCoarse(f32) -> f32 */
+    /* fn dpdxCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[309],
+    /* overloads */ &kOverloads[371],
   },
   {
-    /* [59] */
-    /* fn reflect<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[429],
-  },
-  {
-    /* [60] */
-    /* fn refract<N : num>(vec<N, f32>, vec<N, f32>, f32) -> vec<N, f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[428],
-  },
-  {
-    /* [61] */
-    /* fn reverseBits<T : iu32>(T) -> T */
-    /* fn reverseBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
+    /* [27] */
+    /* fn dpdxFine(f32) -> f32 */
+    /* fn dpdxFine<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[315],
+    /* overloads */ &kOverloads[367],
   },
   {
-    /* [62] */
-    /* fn round(f32) -> f32 */
-    /* fn round<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [28] */
+    /* fn dpdy(f32) -> f32 */
+    /* fn dpdy<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[317],
+    /* overloads */ &kOverloads[347],
   },
   {
-    /* [63] */
-    /* fn select<T : scalar>(T, T, bool) -> T */
-    /* fn select<T : scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
-    /* fn select<N : num, T : scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
-    /* num overloads */ 3,
-    /* overloads */ &kOverloads[251],
-  },
-  {
-    /* [64] */
-    /* fn sign(f32) -> f32 */
-    /* fn sign<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[321],
-  },
-  {
-    /* [65] */
-    /* fn sin(f32) -> f32 */
-    /* fn sin<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[323],
-  },
-  {
-    /* [66] */
-    /* fn sinh(f32) -> f32 */
-    /* fn sinh<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[325],
-  },
-  {
-    /* [67] */
-    /* fn smoothstep(f32, f32, f32) -> f32 */
-    /* fn smoothstep<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[327],
-  },
-  {
-    /* [68] */
-    /* fn sqrt(f32) -> f32 */
-    /* fn sqrt<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[329],
-  },
-  {
-    /* [69] */
-    /* fn step(f32, f32) -> f32 */
-    /* fn step<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[331],
-  },
-  {
-    /* [70] */
-    /* fn storageBarrier() */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[427],
-  },
-  {
-    /* [71] */
-    /* fn tan(f32) -> f32 */
-    /* fn tan<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[335],
-  },
-  {
-    /* [72] */
-    /* fn tanh(f32) -> f32 */
-    /* fn tanh<N : num>(vec<N, f32>) -> vec<N, f32> */
-    /* num overloads */ 2,
-    /* overloads */ &kOverloads[337],
-  },
-  {
-    /* [73] */
-    /* fn transpose<M : num, N : num>(mat<M, N, f32>) -> mat<N, M, f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[426],
-  },
-  {
-    /* [74] */
-    /* fn trunc(f32) -> f32 */
-    /* fn trunc<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* [29] */
+    /* fn dpdyCoarse(f32) -> f32 */
+    /* fn dpdyCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 2,
     /* overloads */ &kOverloads[341],
   },
   {
-    /* [75] */
-    /* fn unpack2x16float(u32) -> vec2<f32> */
+    /* [30] */
+    /* fn dpdyFine(f32) -> f32 */
+    /* fn dpdyFine<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[339],
+  },
+  {
+    /* [31] */
+    /* fn exp(f32) -> f32 */
+    /* fn exp<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[333],
+  },
+  {
+    /* [32] */
+    /* fn exp2(f32) -> f32 */
+    /* fn exp2<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[331],
+  },
+  {
+    /* [33] */
+    /* fn extractBits<T : iu32>(T, u32, u32) -> T */
+    /* fn extractBits<N : num, T : iu32>(vec<N, T>, u32, u32) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[329],
+  },
+  {
+    /* [34] */
+    /* fn faceForward<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[425],
+    /* overloads */ &kOverloads[466],
+  },
+  {
+    /* [35] */
+    /* fn firstLeadingBit<T : iu32>(T) -> T */
+    /* fn firstLeadingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[327],
+  },
+  {
+    /* [36] */
+    /* fn firstTrailingBit<T : iu32>(T) -> T */
+    /* fn firstTrailingBit<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[361],
+  },
+  {
+    /* [37] */
+    /* fn floor(f32) -> f32 */
+    /* fn floor<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[323],
+  },
+  {
+    /* [38] */
+    /* fn fma(f32, f32, f32) -> f32 */
+    /* fn fma<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[319],
+  },
+  {
+    /* [39] */
+    /* fn fract(f32) -> f32 */
+    /* fn fract<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[299],
+  },
+  {
+    /* [40] */
+    /* fn frexp(f32) -> __frexp_result */
+    /* fn frexp<N : num>(vec<N, f32>) -> __frexp_result_vec<N> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[295],
+  },
+  {
+    /* [41] */
+    /* fn fwidth(f32) -> f32 */
+    /* fn fwidth<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[297],
+  },
+  {
+    /* [42] */
+    /* fn fwidthCoarse(f32) -> f32 */
+    /* fn fwidthCoarse<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[325],
+  },
+  {
+    /* [43] */
+    /* fn fwidthFine(f32) -> f32 */
+    /* fn fwidthFine<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[301],
+  },
+  {
+    /* [44] */
+    /* fn insertBits<T : iu32>(T, T, u32, u32) -> T */
+    /* fn insertBits<N : num, T : iu32>(vec<N, T>, vec<N, T>, u32, u32) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[303],
+  },
+  {
+    /* [45] */
+    /* fn inverseSqrt(f32) -> f32 */
+    /* fn inverseSqrt<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[305],
+  },
+  {
+    /* [46] */
+    /* fn ldexp(f32, i32) -> f32 */
+    /* fn ldexp<N : num>(vec<N, f32>, vec<N, i32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[307],
+  },
+  {
+    /* [47] */
+    /* fn length(f32) -> f32 */
+    /* fn length<N : num>(vec<N, f32>) -> f32 */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[309],
+  },
+  {
+    /* [48] */
+    /* fn log(f32) -> f32 */
+    /* fn log<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[311],
+  },
+  {
+    /* [49] */
+    /* fn log2(f32) -> f32 */
+    /* fn log2<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[313],
+  },
+  {
+    /* [50] */
+    /* fn max<T : fiu32>(T, T) -> T */
+    /* fn max<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[315],
+  },
+  {
+    /* [51] */
+    /* fn min<T : fiu32>(T, T) -> T */
+    /* fn min<N : num, T : fiu32>(vec<N, T>, vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[317],
+  },
+  {
+    /* [52] */
+    /* fn mix(f32, f32, f32) -> f32 */
+    /* fn mix<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* fn mix<N : num>(vec<N, f32>, vec<N, f32>, f32) -> vec<N, f32> */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[290],
+  },
+  {
+    /* [53] */
+    /* fn modf(f32) -> __modf_result */
+    /* fn modf<N : num>(vec<N, f32>) -> __modf_result_vec<N> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[321],
+  },
+  {
+    /* [54] */
+    /* fn normalize<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[464],
+  },
+  {
+    /* [55] */
+    /* fn pack2x16float(vec2<f32>) -> u32 */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[472],
+  },
+  {
+    /* [56] */
+    /* fn pack2x16snorm(vec2<f32>) -> u32 */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[463],
+  },
+  {
+    /* [57] */
+    /* fn pack2x16unorm(vec2<f32>) -> u32 */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[462],
+  },
+  {
+    /* [58] */
+    /* fn pack4x8snorm(vec4<f32>) -> u32 */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[461],
+  },
+  {
+    /* [59] */
+    /* fn pack4x8unorm(vec4<f32>) -> u32 */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[460],
+  },
+  {
+    /* [60] */
+    /* fn pow(f32, f32) -> f32 */
+    /* fn pow<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[335],
+  },
+  {
+    /* [61] */
+    /* fn radians(f32) -> f32 */
+    /* fn radians<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[337],
+  },
+  {
+    /* [62] */
+    /* fn reflect<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[459],
+  },
+  {
+    /* [63] */
+    /* fn refract<N : num>(vec<N, f32>, vec<N, f32>, f32) -> vec<N, f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[458],
+  },
+  {
+    /* [64] */
+    /* fn reverseBits<T : iu32>(T) -> T */
+    /* fn reverseBits<N : num, T : iu32>(vec<N, T>) -> vec<N, T> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[343],
+  },
+  {
+    /* [65] */
+    /* fn round(f32) -> f32 */
+    /* fn round<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[345],
+  },
+  {
+    /* [66] */
+    /* fn select<T : scalar_no_f16>(T, T, bool) -> T */
+    /* fn select<T : scalar_no_f16, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
+    /* fn select<N : num, T : scalar_no_f16>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[284],
+  },
+  {
+    /* [67] */
+    /* fn sign(f32) -> f32 */
+    /* fn sign<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[349],
+  },
+  {
+    /* [68] */
+    /* fn sin(f32) -> f32 */
+    /* fn sin<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[351],
+  },
+  {
+    /* [69] */
+    /* fn sinh(f32) -> f32 */
+    /* fn sinh<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[353],
+  },
+  {
+    /* [70] */
+    /* fn smoothstep(f32, f32, f32) -> f32 */
+    /* fn smoothstep<N : num>(vec<N, f32>, vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[355],
+  },
+  {
+    /* [71] */
+    /* fn sqrt(f32) -> f32 */
+    /* fn sqrt<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[357],
+  },
+  {
+    /* [72] */
+    /* fn step(f32, f32) -> f32 */
+    /* fn step<N : num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[359],
+  },
+  {
+    /* [73] */
+    /* fn storageBarrier() */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[450],
+  },
+  {
+    /* [74] */
+    /* fn tan(f32) -> f32 */
+    /* fn tan<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[363],
+  },
+  {
+    /* [75] */
+    /* fn tanh(f32) -> f32 */
+    /* fn tanh<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[365],
   },
   {
     /* [76] */
-    /* fn unpack2x16snorm(u32) -> vec2<f32> */
+    /* fn transpose<M : num, N : num>(mat<M, N, f32>) -> mat<N, M, f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[407],
+    /* overloads */ &kOverloads[457],
   },
   {
     /* [77] */
-    /* fn unpack2x16unorm(u32) -> vec2<f32> */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[423],
+    /* fn trunc(f32) -> f32 */
+    /* fn trunc<N : num>(vec<N, f32>) -> vec<N, f32> */
+    /* num overloads */ 2,
+    /* overloads */ &kOverloads[369],
   },
   {
     /* [78] */
-    /* fn unpack4x8snorm(u32) -> vec4<f32> */
+    /* fn unpack2x16float(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[422],
+    /* overloads */ &kOverloads[456],
   },
   {
     /* [79] */
-    /* fn unpack4x8unorm(u32) -> vec4<f32> */
+    /* fn unpack2x16snorm(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[421],
+    /* overloads */ &kOverloads[455],
   },
   {
     /* [80] */
-    /* fn workgroupBarrier() */
+    /* fn unpack2x16unorm(u32) -> vec2<f32> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[420],
+    /* overloads */ &kOverloads[454],
   },
   {
     /* [81] */
+    /* fn unpack4x8snorm(u32) -> vec4<f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[453],
+  },
+  {
+    /* [82] */
+    /* fn unpack4x8unorm(u32) -> vec4<f32> */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[452],
+  },
+  {
+    /* [83] */
+    /* fn workgroupBarrier() */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[451],
+  },
+  {
+    /* [84] */
     /* fn textureDimensions<T : fiu32>(texture: texture_1d<T>) -> i32 */
     /* fn textureDimensions<T : fiu32>(texture: texture_1d<T>, level: i32) -> i32 */
     /* fn textureDimensions<T : fiu32>(texture: texture_2d<T>) -> vec2<i32> */
@@ -13539,7 +14275,7 @@
     /* overloads */ &kOverloads[0],
   },
   {
-    /* [82] */
+    /* [85] */
     /* fn textureGather<T : fiu32>(@const component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>) -> vec4<T> */
     /* fn textureGather<T : fiu32>(@const component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>, @const offset: vec2<i32>) -> vec4<T> */
     /* fn textureGather<T : fiu32>(@const component: i32, texture: texture_2d_array<T>, sampler: sampler, coords: vec2<f32>, array_index: i32) -> vec4<T> */
@@ -13553,10 +14289,10 @@
     /* fn textureGather(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> vec4<f32> */
     /* fn textureGather(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: i32) -> vec4<f32> */
     /* num overloads */ 12,
-    /* overloads */ &kOverloads[71],
+    /* overloads */ &kOverloads[72],
   },
   {
-    /* [83] */
+    /* [86] */
     /* fn textureGatherCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32> */
     /* fn textureGatherCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureGatherCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32) -> vec4<f32> */
@@ -13564,20 +14300,20 @@
     /* fn textureGatherCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> vec4<f32> */
     /* fn textureGatherCompare(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: i32, depth_ref: f32) -> vec4<f32> */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[157],
+    /* overloads */ &kOverloads[235],
   },
   {
-    /* [84] */
+    /* [87] */
     /* fn textureNumLayers<T : fiu32>(texture: texture_2d_array<T>) -> i32 */
     /* fn textureNumLayers<T : fiu32>(texture: texture_cube_array<T>) -> i32 */
     /* fn textureNumLayers(texture: texture_depth_2d_array) -> i32 */
     /* fn textureNumLayers(texture: texture_depth_cube_array) -> i32 */
     /* fn textureNumLayers<F : texel_format, A : write_only>(texture: texture_storage_2d_array<F, A>) -> i32 */
     /* num overloads */ 5,
-    /* overloads */ &kOverloads[185],
+    /* overloads */ &kOverloads[246],
   },
   {
-    /* [85] */
+    /* [88] */
     /* fn textureNumLevels<T : fiu32>(texture: texture_1d<T>) -> i32 */
     /* fn textureNumLevels<T : fiu32>(texture: texture_2d<T>) -> i32 */
     /* fn textureNumLevels<T : fiu32>(texture: texture_2d_array<T>) -> i32 */
@@ -13589,17 +14325,17 @@
     /* fn textureNumLevels(texture: texture_depth_cube) -> i32 */
     /* fn textureNumLevels(texture: texture_depth_cube_array) -> i32 */
     /* num overloads */ 10,
-    /* overloads */ &kOverloads[105],
+    /* overloads */ &kOverloads[107],
   },
   {
-    /* [86] */
+    /* [89] */
     /* fn textureNumSamples<T : fiu32>(texture: texture_multisampled_2d<T>) -> i32 */
     /* fn textureNumSamples(texture: texture_depth_multisampled_2d) -> i32 */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[365],
+    /* overloads */ &kOverloads[393],
   },
   {
-    /* [87] */
+    /* [90] */
     /* fn textureSample(texture: texture_1d<f32>, sampler: sampler, coords: f32) -> vec4<f32> */
     /* fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>) -> vec4<f32> */
     /* fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, @const offset: vec2<i32>) -> vec4<f32> */
@@ -13616,10 +14352,10 @@
     /* fn textureSample(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> f32 */
     /* fn textureSample(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: i32) -> f32 */
     /* num overloads */ 15,
-    /* overloads */ &kOverloads[27],
+    /* overloads */ &kOverloads[42],
   },
   {
-    /* [88] */
+    /* [91] */
     /* fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32> */
     /* fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureSampleBias(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, bias: f32) -> vec4<f32> */
@@ -13629,10 +14365,10 @@
     /* fn textureSampleBias(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32> */
     /* fn textureSampleBias(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: i32, bias: f32) -> vec4<f32> */
     /* num overloads */ 8,
-    /* overloads */ &kOverloads[133],
+    /* overloads */ &kOverloads[152],
   },
   {
-    /* [89] */
+    /* [92] */
     /* fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, @const offset: vec2<i32>) -> f32 */
     /* fn textureSampleCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32) -> f32 */
@@ -13640,10 +14376,10 @@
     /* fn textureSampleCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompare(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: i32, depth_ref: f32) -> f32 */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[163],
+    /* overloads */ &kOverloads[223],
   },
   {
-    /* [90] */
+    /* [93] */
     /* fn textureSampleCompareLevel(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompareLevel(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, @const offset: vec2<i32>) -> f32 */
     /* fn textureSampleCompareLevel(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32) -> f32 */
@@ -13651,10 +14387,10 @@
     /* fn textureSampleCompareLevel(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32 */
     /* fn textureSampleCompareLevel(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: i32, depth_ref: f32) -> f32 */
     /* num overloads */ 6,
-    /* overloads */ &kOverloads[169],
+    /* overloads */ &kOverloads[229],
   },
   {
-    /* [91] */
+    /* [94] */
     /* fn textureSampleGrad(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32> */
     /* fn textureSampleGrad(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureSampleGrad(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32> */
@@ -13664,10 +14400,10 @@
     /* fn textureSampleGrad(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> */
     /* fn textureSampleGrad(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: i32, ddx: vec3<f32>, ddy: vec3<f32>) -> vec4<f32> */
     /* num overloads */ 8,
-    /* overloads */ &kOverloads[141],
+    /* overloads */ &kOverloads[144],
   },
   {
-    /* [92] */
+    /* [95] */
     /* fn textureSampleLevel(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, level: f32) -> vec4<f32> */
     /* fn textureSampleLevel(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, level: f32, @const offset: vec2<i32>) -> vec4<f32> */
     /* fn textureSampleLevel(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, level: f32) -> vec4<f32> */
@@ -13684,10 +14420,10 @@
     /* fn textureSampleLevel(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: i32, level: i32) -> f32 */
     /* fn textureSampleLevel(texture: texture_external, sampler: sampler, coords: vec2<f32>) -> vec4<f32> */
     /* num overloads */ 15,
-    /* overloads */ &kOverloads[42],
+    /* overloads */ &kOverloads[57],
   },
   {
-    /* [93] */
+    /* [96] */
     /* fn textureStore(texture: texture_storage_1d<f32_texel_format, write>, coords: i32, value: vec4<f32>) */
     /* fn textureStore(texture: texture_storage_2d<f32_texel_format, write>, coords: vec2<i32>, value: vec4<f32>) */
     /* fn textureStore(texture: texture_storage_2d_array<f32_texel_format, write>, coords: vec2<i32>, array_index: i32, value: vec4<f32>) */
@@ -13701,10 +14437,10 @@
     /* fn textureStore(texture: texture_storage_2d_array<u32_texel_format, write>, coords: vec2<i32>, array_index: i32, value: vec4<u32>) */
     /* fn textureStore(texture: texture_storage_3d<u32_texel_format, write>, coords: vec3<i32>, value: vec4<u32>) */
     /* num overloads */ 12,
-    /* overloads */ &kOverloads[83],
+    /* overloads */ &kOverloads[84],
   },
   {
-    /* [94] */
+    /* [97] */
     /* fn textureLoad<T : fiu32>(texture: texture_1d<T>, coords: i32, level: i32) -> vec4<T> */
     /* fn textureLoad<T : fiu32>(texture: texture_2d<T>, coords: vec2<i32>, level: i32) -> vec4<T> */
     /* fn textureLoad<T : fiu32>(texture: texture_2d_array<T>, coords: vec2<i32>, array_index: i32, level: i32) -> vec4<T> */
@@ -13715,73 +14451,73 @@
     /* fn textureLoad(texture: texture_depth_multisampled_2d, coords: vec2<i32>, sample_index: i32) -> f32 */
     /* fn textureLoad(texture: texture_external, coords: vec2<i32>) -> vec4<f32> */
     /* num overloads */ 9,
-    /* overloads */ &kOverloads[115],
-  },
-  {
-    /* [95] */
-    /* fn atomicLoad<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>) -> T */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[416],
-  },
-  {
-    /* [96] */
-    /* fn atomicStore<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[415],
-  },
-  {
-    /* [97] */
-    /* fn atomicAdd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
-    /* num overloads */ 1,
-    /* overloads */ &kOverloads[414],
+    /* overloads */ &kOverloads[126],
   },
   {
     /* [98] */
-    /* fn atomicSub<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicLoad<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[413],
+    /* overloads */ &kOverloads[449],
   },
   {
     /* [99] */
-    /* fn atomicMax<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicStore<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[412],
+    /* overloads */ &kOverloads[448],
   },
   {
     /* [100] */
-    /* fn atomicMin<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicAdd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[411],
+    /* overloads */ &kOverloads[445],
   },
   {
     /* [101] */
-    /* fn atomicAnd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicSub<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[410],
+    /* overloads */ &kOverloads[444],
   },
   {
     /* [102] */
-    /* fn atomicOr<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicMax<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[409],
+    /* overloads */ &kOverloads[443],
   },
   {
     /* [103] */
-    /* fn atomicXor<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicMin<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[408],
+    /* overloads */ &kOverloads[442],
   },
   {
     /* [104] */
-    /* fn atomicExchange<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* fn atomicAnd<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[424],
+    /* overloads */ &kOverloads[441],
   },
   {
     /* [105] */
+    /* fn atomicOr<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[440],
+  },
+  {
+    /* [106] */
+    /* fn atomicXor<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[439],
+  },
+  {
+    /* [107] */
+    /* fn atomicExchange<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T) -> T */
+    /* num overloads */ 1,
+    /* overloads */ &kOverloads[438],
+  },
+  {
+    /* [108] */
     /* fn atomicCompareExchangeWeak<T : iu32, S : workgroup_or_storage>(ptr<S, atomic<T>, read_write>, T, T) -> __atomic_compare_exchange_result<T> */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[442],
+    /* overloads */ &kOverloads[437],
   },
 };
 
@@ -13791,21 +14527,21 @@
     /* op !(bool) -> bool */
     /* op !<N : num>(vec<N, bool>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[269],
+    /* overloads */ &kOverloads[433],
   },
   {
     /* [1] */
     /* op ~<T : iu32>(T) -> T */
     /* op ~<T : iu32, N : num>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[401],
+    /* overloads */ &kOverloads[293],
   },
   {
     /* [2] */
     /* op -<T : fi32>(T) -> T */
     /* op -<T : fi32, N : num>(vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[399],
+    /* overloads */ &kOverloads[431],
   },
 };
 constexpr uint8_t kUnaryOperatorNot = 0;
@@ -13821,7 +14557,7 @@
     /* op +<T : fiu32, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* op +<N : num, M : num>(mat<N, M, f32>, mat<N, M, f32>) -> mat<N, M, f32> */
     /* num overloads */ 5,
-    /* overloads */ &kOverloads[215],
+    /* overloads */ &kOverloads[251],
   },
   {
     /* [1] */
@@ -13831,7 +14567,7 @@
     /* op -<T : fiu32, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* op -<N : num, M : num>(mat<N, M, f32>, mat<N, M, f32>) -> mat<N, M, f32> */
     /* num overloads */ 5,
-    /* overloads */ &kOverloads[210],
+    /* overloads */ &kOverloads[241],
   },
   {
     /* [2] */
@@ -13845,7 +14581,7 @@
     /* op *<C : num, R : num>(vec<R, f32>, mat<C, R, f32>) -> vec<C, f32> */
     /* op *<K : num, C : num, R : num>(mat<K, R, f32>, mat<C, K, f32>) -> mat<C, R, f32> */
     /* num overloads */ 9,
-    /* overloads */ &kOverloads[124],
+    /* overloads */ &kOverloads[117],
   },
   {
     /* [3] */
@@ -13854,7 +14590,7 @@
     /* op /<T : fiu32, N : num>(vec<N, T>, T) -> vec<N, T> */
     /* op /<T : fiu32, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[243],
+    /* overloads */ &kOverloads[268],
   },
   {
     /* [4] */
@@ -13863,14 +14599,14 @@
     /* op %<T : fiu32, N : num>(vec<N, T>, T) -> vec<N, T> */
     /* op %<T : fiu32, N : num>(T, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[239],
+    /* overloads */ &kOverloads[256],
   },
   {
     /* [5] */
     /* op ^<T : iu32>(T, T) -> T */
     /* op ^<T : iu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[387],
+    /* overloads */ &kOverloads[419],
   },
   {
     /* [6] */
@@ -13879,7 +14615,7 @@
     /* op &<T : iu32>(T, T) -> T */
     /* op &<T : iu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[235],
+    /* overloads */ &kOverloads[260],
   },
   {
     /* [7] */
@@ -13888,75 +14624,75 @@
     /* op |<T : iu32>(T, T) -> T */
     /* op |<T : iu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, T> */
     /* num overloads */ 4,
-    /* overloads */ &kOverloads[247],
+    /* overloads */ &kOverloads[264],
   },
   {
     /* [8] */
     /* op &&(bool, bool) -> bool */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[417],
+    /* overloads */ &kOverloads[446],
   },
   {
     /* [9] */
     /* op ||(bool, bool) -> bool */
     /* num overloads */ 1,
-    /* overloads */ &kOverloads[418],
+    /* overloads */ &kOverloads[447],
   },
   {
     /* [10] */
     /* op ==<T : scalar>(T, T) -> bool */
     /* op ==<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[369],
+    /* overloads */ &kOverloads[413],
   },
   {
     /* [11] */
     /* op !=<T : scalar>(T, T) -> bool */
     /* op !=<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[353],
+    /* overloads */ &kOverloads[411],
   },
   {
     /* [12] */
     /* op <<T : fiu32>(T, T) -> bool */
     /* op <<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[351],
+    /* overloads */ &kOverloads[397],
   },
   {
     /* [13] */
     /* op ><T : fiu32>(T, T) -> bool */
     /* op ><T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[349],
+    /* overloads */ &kOverloads[381],
   },
   {
     /* [14] */
     /* op <=<T : fiu32>(T, T) -> bool */
     /* op <=<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[347],
+    /* overloads */ &kOverloads[379],
   },
   {
     /* [15] */
     /* op >=<T : fiu32>(T, T) -> bool */
     /* op >=<T : fiu32, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[345],
+    /* overloads */ &kOverloads[377],
   },
   {
     /* [16] */
     /* op <<<T : iu32>(T, u32) -> T */
     /* op <<<T : iu32, N : num>(vec<N, T>, vec<N, u32>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[405],
+    /* overloads */ &kOverloads[435],
   },
   {
     /* [17] */
     /* op >><T : iu32>(T, u32) -> T */
     /* op >><T : iu32, N : num>(vec<N, T>, vec<N, u32>) -> vec<N, T> */
     /* num overloads */ 2,
-    /* overloads */ &kOverloads[339],
+    /* overloads */ &kOverloads[373],
   },
 };
 constexpr uint8_t kBinaryOperatorPlus = 0;
@@ -13985,7 +14721,7 @@
     /* ctor i32(i32) -> i32 */
     /* conv i32<T : scalar_no_i32>(T) -> i32 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[254],
+    /* overloads */ &kOverloads[272],
   },
   {
     /* [1] */
@@ -13993,7 +14729,7 @@
     /* ctor u32(u32) -> u32 */
     /* conv u32<T : scalar_no_u32>(T) -> u32 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[257],
+    /* overloads */ &kOverloads[287],
   },
   {
     /* [2] */
@@ -14001,31 +14737,40 @@
     /* ctor f32(f32) -> f32 */
     /* conv f32<T : scalar_no_f32>(T) -> f32 */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[260],
+    /* overloads */ &kOverloads[281],
   },
   {
     /* [3] */
+    /* ctor f16() -> f16 */
+    /* ctor f16(f16) -> f16 */
+    /* conv f16<T : scalar_no_f16>(T) -> f16 */
+    /* num overloads */ 3,
+    /* overloads */ &kOverloads[278],
+  },
+  {
+    /* [4] */
     /* ctor bool() -> bool */
     /* ctor bool(bool) -> bool */
     /* conv bool<T : scalar_no_bool>(T) -> bool */
     /* num overloads */ 3,
-    /* overloads */ &kOverloads[263],
+    /* overloads */ &kOverloads[275],
   },
   {
-    /* [4] */
+    /* [5] */
     /* ctor vec2<T : scalar>() -> vec2<T> */
     /* ctor vec2<T : scalar>(vec2<T>) -> vec2<T> */
     /* ctor vec2<T : abstract_or_scalar>(T) -> vec2<T> */
     /* ctor vec2<T : abstract_or_scalar>(x: T, y: T) -> vec2<T> */
     /* conv vec2<T : f32, U : scalar_no_f32>(vec2<U>) -> vec2<f32> */
+    /* conv vec2<T : f16, U : scalar_no_f16>(vec2<U>) -> vec2<f16> */
     /* conv vec2<T : i32, U : scalar_no_i32>(vec2<U>) -> vec2<i32> */
     /* conv vec2<T : u32, U : scalar_no_u32>(vec2<U>) -> vec2<u32> */
     /* conv vec2<T : bool, U : scalar_no_bool>(vec2<U>) -> vec2<bool> */
-    /* num overloads */ 8,
-    /* overloads */ &kOverloads[149],
+    /* num overloads */ 9,
+    /* overloads */ &kOverloads[135],
   },
   {
-    /* [5] */
+    /* [6] */
     /* ctor vec3<T : scalar>() -> vec3<T> */
     /* ctor vec3<T : scalar>(vec3<T>) -> vec3<T> */
     /* ctor vec3<T : abstract_or_scalar>(T) -> vec3<T> */
@@ -14033,14 +14778,15 @@
     /* ctor vec3<T : abstract_or_scalar>(xy: vec2<T>, z: T) -> vec3<T> */
     /* ctor vec3<T : abstract_or_scalar>(x: T, yz: vec2<T>) -> vec3<T> */
     /* conv vec3<T : f32, U : scalar_no_f32>(vec3<U>) -> vec3<f32> */
+    /* conv vec3<T : f16, U : scalar_no_f16>(vec3<U>) -> vec3<f16> */
     /* conv vec3<T : i32, U : scalar_no_i32>(vec3<U>) -> vec3<i32> */
     /* conv vec3<T : u32, U : scalar_no_u32>(vec3<U>) -> vec3<u32> */
     /* conv vec3<T : bool, U : scalar_no_bool>(vec3<U>) -> vec3<bool> */
-    /* num overloads */ 10,
-    /* overloads */ &kOverloads[95],
+    /* num overloads */ 11,
+    /* overloads */ &kOverloads[96],
   },
   {
-    /* [6] */
+    /* [7] */
     /* ctor vec4<T : scalar>() -> vec4<T> */
     /* ctor vec4<T : scalar>(vec4<T>) -> vec4<T> */
     /* ctor vec4<T : abstract_or_scalar>(T) -> vec4<T> */
@@ -14052,101 +14798,120 @@
     /* ctor vec4<T : abstract_or_scalar>(xyz: vec3<T>, w: T) -> vec4<T> */
     /* ctor vec4<T : abstract_or_scalar>(x: T, zyw: vec3<T>) -> vec4<T> */
     /* conv vec4<T : f32, U : scalar_no_f32>(vec4<U>) -> vec4<f32> */
+    /* conv vec4<T : f16, U : scalar_no_f16>(vec4<U>) -> vec4<f16> */
     /* conv vec4<T : i32, U : scalar_no_i32>(vec4<U>) -> vec4<i32> */
     /* conv vec4<T : u32, U : scalar_no_u32>(vec4<U>) -> vec4<u32> */
     /* conv vec4<T : bool, U : scalar_no_bool>(vec4<U>) -> vec4<bool> */
-    /* num overloads */ 14,
-    /* overloads */ &kOverloads[57],
-  },
-  {
-    /* [7] */
-    /* ctor mat2x2() -> mat2x2<f32> */
-    /* ctor mat2x2<f32>(mat2x2<f32>) -> mat2x2<f32> */
-    /* ctor mat2x2<T : af_f32>(T) -> mat2x2<T> */
-    /* ctor mat2x2<T : af_f32>(T, T, T, T) -> mat2x2<T> */
-    /* ctor mat2x2<T : af_f32>(vec2<T>, vec2<T>) -> mat2x2<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[190],
+    /* num overloads */ 15,
+    /* overloads */ &kOverloads[27],
   },
   {
     /* [8] */
-    /* ctor mat2x3() -> mat2x3<f32> */
-    /* ctor mat2x3<f32>(mat2x3<f32>) -> mat2x3<f32> */
-    /* ctor mat2x3<T : af_f32>(T) -> mat2x3<T> */
-    /* ctor mat2x3<T : af_f32>(T, T, T, T, T, T) -> mat2x3<T> */
-    /* ctor mat2x3<T : af_f32>(vec3<T>, vec3<T>) -> mat2x3<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[180],
+    /* ctor mat2x2<T : f32f16>() -> mat2x2<T> */
+    /* ctor mat2x2<T : f32f16>(mat2x2<T>) -> mat2x2<T> */
+    /* ctor mat2x2<T : af_f32>(T) -> mat2x2<T> */
+    /* ctor mat2x2<T : af_f32f16>(T, T, T, T) -> mat2x2<T> */
+    /* ctor mat2x2<T : af_f32f16>(vec2<T>, vec2<T>) -> mat2x2<T> */
+    /* conv mat2x2<T : f16>(mat2x2<f32>) -> mat2x2<f16> */
+    /* conv mat2x2<T : f32>(mat2x2<f16>) -> mat2x2<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[181],
   },
   {
     /* [9] */
-    /* ctor mat2x4() -> mat2x4<f32> */
-    /* ctor mat2x4<f32>(mat2x4<f32>) -> mat2x4<f32> */
-    /* ctor mat2x4<T : af_f32>(T) -> mat2x4<T> */
-    /* ctor mat2x4<T : af_f32>(T, T, T, T, T, T, T, T) -> mat2x4<T> */
-    /* ctor mat2x4<T : af_f32>(vec4<T>, vec4<T>) -> mat2x4<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[225],
+    /* ctor mat2x3<T : f32f16>() -> mat2x3<T> */
+    /* ctor mat2x3<T : f32f16>(mat2x3<T>) -> mat2x3<T> */
+    /* ctor mat2x3<T : af_f32>(T) -> mat2x3<T> */
+    /* ctor mat2x3<T : af_f32f16>(T, T, T, T, T, T) -> mat2x3<T> */
+    /* ctor mat2x3<T : af_f32f16>(vec3<T>, vec3<T>) -> mat2x3<T> */
+    /* conv mat2x3<T : f16>(mat2x3<f32>) -> mat2x3<f16> */
+    /* conv mat2x3<T : f32>(mat2x3<f16>) -> mat2x3<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[202],
   },
   {
     /* [10] */
-    /* ctor mat3x2() -> mat3x2<f32> */
-    /* ctor mat3x2<f32>(mat3x2<f32>) -> mat3x2<f32> */
-    /* ctor mat3x2<T : af_f32>(T) -> mat3x2<T> */
-    /* ctor mat3x2<T : af_f32>(T, T, T, T, T, T) -> mat3x2<T> */
-    /* ctor mat3x2<T : af_f32>(vec2<T>, vec2<T>, vec2<T>) -> mat3x2<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[200],
+    /* ctor mat2x4<T : f32f16>() -> mat2x4<T> */
+    /* ctor mat2x4<T : f32f16>(mat2x4<T>) -> mat2x4<T> */
+    /* ctor mat2x4<T : af_f32>(T) -> mat2x4<T> */
+    /* ctor mat2x4<T : af_f32f16>(T, T, T, T, T, T, T, T) -> mat2x4<T> */
+    /* ctor mat2x4<T : af_f32f16>(vec4<T>, vec4<T>) -> mat2x4<T> */
+    /* conv mat2x4<T : f16>(mat2x4<f32>) -> mat2x4<f16> */
+    /* conv mat2x4<T : f32>(mat2x4<f16>) -> mat2x4<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[174],
   },
   {
     /* [11] */
-    /* ctor mat3x3() -> mat3x3<f32> */
-    /* ctor mat3x3<f32>(mat3x3<f32>) -> mat3x3<f32> */
-    /* ctor mat3x3<T : af_f32>(T) -> mat3x3<T> */
-    /* ctor mat3x3<T : af_f32>(T, T, T, T, T, T, T, T, T) -> mat3x3<T> */
-    /* ctor mat3x3<T : af_f32>(vec3<T>, vec3<T>, vec3<T>) -> mat3x3<T> */
-    /* num overloads */ 5,
+    /* ctor mat3x2<T : f32f16>() -> mat3x2<T> */
+    /* ctor mat3x2<T : f32f16>(mat3x2<T>) -> mat3x2<T> */
+    /* ctor mat3x2<T : af_f32>(T) -> mat3x2<T> */
+    /* ctor mat3x2<T : af_f32f16>(T, T, T, T, T, T) -> mat3x2<T> */
+    /* ctor mat3x2<T : af_f32f16>(vec2<T>, vec2<T>, vec2<T>) -> mat3x2<T> */
+    /* conv mat3x2<T : f16>(mat3x2<f32>) -> mat3x2<f16> */
+    /* conv mat3x2<T : f32>(mat3x2<f16>) -> mat3x2<f32> */
+    /* num overloads */ 7,
     /* overloads */ &kOverloads[195],
   },
   {
     /* [12] */
-    /* ctor mat3x4() -> mat3x4<f32> */
-    /* ctor mat3x4<f32>(mat3x4<f32>) -> mat3x4<f32> */
-    /* ctor mat3x4<T : af_f32>(T) -> mat3x4<T> */
-    /* ctor mat3x4<T : af_f32>(T, T, T, T, T, T, T, T, T, T, T, T) -> mat3x4<T> */
-    /* ctor mat3x4<T : af_f32>(vec4<T>, vec4<T>, vec4<T>) -> mat3x4<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[230],
+    /* ctor mat3x3<T : f32f16>() -> mat3x3<T> */
+    /* ctor mat3x3<T : f32f16>(mat3x3<T>) -> mat3x3<T> */
+    /* ctor mat3x3<T : af_f32>(T) -> mat3x3<T> */
+    /* ctor mat3x3<T : af_f32f16>(T, T, T, T, T, T, T, T, T) -> mat3x3<T> */
+    /* ctor mat3x3<T : af_f32f16>(vec3<T>, vec3<T>, vec3<T>) -> mat3x3<T> */
+    /* conv mat3x3<T : f16>(mat3x3<f32>) -> mat3x3<f16> */
+    /* conv mat3x3<T : f32>(mat3x3<f16>) -> mat3x3<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[188],
   },
   {
     /* [13] */
-    /* ctor mat4x2() -> mat4x2<f32> */
-    /* ctor mat4x2<f32>(mat4x2<f32>) -> mat4x2<f32> */
-    /* ctor mat4x2<T : af_f32>(T) -> mat4x2<T> */
-    /* ctor mat4x2<T : af_f32>(T, T, T, T, T, T, T, T) -> mat4x2<T> */
-    /* ctor mat4x2<T : af_f32>(vec2<T>, vec2<T>, vec2<T>, vec2<T>) -> mat4x2<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[175],
+    /* ctor mat3x4<T : f32f16>() -> mat3x4<T> */
+    /* ctor mat3x4<T : f32f16>(mat3x4<T>) -> mat3x4<T> */
+    /* ctor mat3x4<T : af_f32>(T) -> mat3x4<T> */
+    /* ctor mat3x4<T : af_f32f16>(T, T, T, T, T, T, T, T, T, T, T, T) -> mat3x4<T> */
+    /* ctor mat3x4<T : af_f32f16>(vec4<T>, vec4<T>, vec4<T>) -> mat3x4<T> */
+    /* conv mat3x4<T : f16>(mat3x4<f32>) -> mat3x4<f16> */
+    /* conv mat3x4<T : f32>(mat3x4<f16>) -> mat3x4<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[216],
   },
   {
     /* [14] */
-    /* ctor mat4x3() -> mat4x3<f32> */
-    /* ctor mat4x3<f32>(mat4x3<f32>) -> mat4x3<f32> */
-    /* ctor mat4x3<T : af_f32>(T) -> mat4x3<T> */
-    /* ctor mat4x3<T : af_f32>(T, T, T, T, T, T, T, T, T, T, T, T) -> mat4x3<T> */
-    /* ctor mat4x3<T : af_f32>(vec3<T>, vec3<T>, vec3<T>, vec3<T>) -> mat4x3<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[205],
+    /* ctor mat4x2<T : f32f16>() -> mat4x2<T> */
+    /* ctor mat4x2<T : f32f16>(mat4x2<T>) -> mat4x2<T> */
+    /* ctor mat4x2<T : af_f32>(T) -> mat4x2<T> */
+    /* ctor mat4x2<T : af_f32f16>(T, T, T, T, T, T, T, T) -> mat4x2<T> */
+    /* ctor mat4x2<T : af_f32f16>(vec2<T>, vec2<T>, vec2<T>, vec2<T>) -> mat4x2<T> */
+    /* conv mat4x2<T : f16>(mat4x2<f32>) -> mat4x2<f16> */
+    /* conv mat4x2<T : f32>(mat4x2<f16>) -> mat4x2<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[167],
   },
   {
     /* [15] */
-    /* ctor mat4x4() -> mat4x4<f32> */
-    /* ctor mat4x4<f32>(mat4x4<f32>) -> mat4x4<f32> */
+    /* ctor mat4x3<T : f32f16>() -> mat4x3<T> */
+    /* ctor mat4x3<T : f32f16>(mat4x3<T>) -> mat4x3<T> */
+    /* ctor mat4x3<T : af_f32>(T) -> mat4x3<T> */
+    /* ctor mat4x3<T : af_f32f16>(T, T, T, T, T, T, T, T, T, T, T, T) -> mat4x3<T> */
+    /* ctor mat4x3<T : af_f32f16>(vec3<T>, vec3<T>, vec3<T>, vec3<T>) -> mat4x3<T> */
+    /* conv mat4x3<T : f16>(mat4x3<f32>) -> mat4x3<f16> */
+    /* conv mat4x3<T : f32>(mat4x3<f16>) -> mat4x3<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[209],
+  },
+  {
+    /* [16] */
+    /* ctor mat4x4<T : f32f16>() -> mat4x4<T> */
+    /* ctor mat4x4<T : f32f16>(mat4x4<T>) -> mat4x4<T> */
     /* ctor mat4x4<T : af_f32>(T) -> mat4x4<T> */
-    /* ctor mat4x4<T : af_f32>(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) -> mat4x4<T> */
-    /* ctor mat4x4<T : af_f32>(vec4<T>, vec4<T>, vec4<T>, vec4<T>) -> mat4x4<T> */
-    /* num overloads */ 5,
-    /* overloads */ &kOverloads[220],
+    /* ctor mat4x4<T : af_f32f16>(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T) -> mat4x4<T> */
+    /* ctor mat4x4<T : af_f32f16>(vec4<T>, vec4<T>, vec4<T>, vec4<T>) -> mat4x4<T> */
+    /* conv mat4x4<T : f16>(mat4x4<f32>) -> mat4x4<f16> */
+    /* conv mat4x4<T : f32>(mat4x4<f16>) -> mat4x4<f32> */
+    /* num overloads */ 7,
+    /* overloads */ &kOverloads[160],
   },
 };
 
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index e2c651e..e2c2247 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -709,18 +709,19 @@
     EXPECT_EQ(Diagnostics().str(), R"(12:34 error: no matching constructor for vec3(i32, f32, i32)
 
 6 candidate constructors:
-  vec3(x: T, y: T, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(xy: vec2<T>, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(x: T, yz: vec2<T>) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(vec3<T>) -> vec3<T>  where: T is f32, i32, u32 or bool
-  vec3() -> vec3<T>  where: T is f32, i32, u32 or bool
+  vec3(x: T, y: T, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(xy: vec2<T>, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(x: T, yz: vec2<T>) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(vec3<T>) -> vec3<T>  where: T is f32, f16, i32, u32 or bool
+  vec3() -> vec3<T>  where: T is f32, f16, i32, u32 or bool
 
-4 candidate conversions:
-  vec3(vec3<U>) -> vec3<f32>  where: T is f32, U is i32, u32 or bool
-  vec3(vec3<U>) -> vec3<i32>  where: T is i32, U is f32, u32 or bool
-  vec3(vec3<U>) -> vec3<u32>  where: T is u32, U is f32, i32 or bool
-  vec3(vec3<U>) -> vec3<bool>  where: T is bool, U is f32, i32 or u32
+5 candidate conversions:
+  vec3(vec3<U>) -> vec3<f32>  where: T is f32, U is i32, f16, u32 or bool
+  vec3(vec3<U>) -> vec3<f16>  where: T is f16, U is f32, i32, u32 or bool
+  vec3(vec3<U>) -> vec3<i32>  where: T is i32, U is f32, f16, u32 or bool
+  vec3(vec3<U>) -> vec3<u32>  where: T is u32, U is f32, f16, i32 or bool
+  vec3(vec3<U>) -> vec3<bool>  where: T is bool, U is f32, f16, i32 or u32
 )");
 }
 
@@ -733,18 +734,19 @@
               R"(12:34 error: no matching constructor for vec3<i32>(i32, f32, i32)
 
 6 candidate constructors:
-  vec3(x: T, y: T, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(x: T, yz: vec2<T>) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(xy: vec2<T>, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(vec3<T>) -> vec3<T>  where: T is f32, i32, u32 or bool
-  vec3() -> vec3<T>  where: T is f32, i32, u32 or bool
+  vec3(x: T, y: T, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(x: T, yz: vec2<T>) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(xy: vec2<T>, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(vec3<T>) -> vec3<T>  where: T is f32, f16, i32, u32 or bool
+  vec3() -> vec3<T>  where: T is f32, f16, i32, u32 or bool
 
-4 candidate conversions:
-  vec3(vec3<U>) -> vec3<f32>  where: T is f32, U is i32, u32 or bool
-  vec3(vec3<U>) -> vec3<i32>  where: T is i32, U is f32, u32 or bool
-  vec3(vec3<U>) -> vec3<u32>  where: T is u32, U is f32, i32 or bool
-  vec3(vec3<U>) -> vec3<bool>  where: T is bool, U is f32, i32 or u32
+5 candidate conversions:
+  vec3(vec3<U>) -> vec3<f32>  where: T is f32, U is i32, f16, u32 or bool
+  vec3(vec3<U>) -> vec3<f16>  where: T is f16, U is f32, i32, u32 or bool
+  vec3(vec3<U>) -> vec3<i32>  where: T is i32, U is f32, f16, u32 or bool
+  vec3(vec3<U>) -> vec3<u32>  where: T is u32, U is f32, f16, i32 or bool
+  vec3(vec3<U>) -> vec3<bool>  where: T is bool, U is f32, f16, i32 or u32
 )");
 }
 
@@ -770,18 +772,19 @@
               R"(12:34 error: no matching constructor for vec3<f32>(array<u32>)
 
 6 candidate constructors:
-  vec3(vec3<T>) -> vec3<T>  where: T is f32, i32, u32 or bool
-  vec3(T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3() -> vec3<T>  where: T is f32, i32, u32 or bool
-  vec3(xy: vec2<T>, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(x: T, yz: vec2<T>) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
-  vec3(x: T, y: T, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, i32, u32 or bool
+  vec3(vec3<T>) -> vec3<T>  where: T is f32, f16, i32, u32 or bool
+  vec3(T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3() -> vec3<T>  where: T is f32, f16, i32, u32 or bool
+  vec3(xy: vec2<T>, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(x: T, yz: vec2<T>) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
+  vec3(x: T, y: T, z: T) -> vec3<T>  where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
 
-4 candidate conversions:
-  vec3(vec3<U>) -> vec3<f32>  where: T is f32, U is i32, u32 or bool
-  vec3(vec3<U>) -> vec3<i32>  where: T is i32, U is f32, u32 or bool
-  vec3(vec3<U>) -> vec3<u32>  where: T is u32, U is f32, i32 or bool
-  vec3(vec3<U>) -> vec3<bool>  where: T is bool, U is f32, i32 or u32
+5 candidate conversions:
+  vec3(vec3<U>) -> vec3<f32>  where: T is f32, U is i32, f16, u32 or bool
+  vec3(vec3<U>) -> vec3<f16>  where: T is f16, U is f32, i32, u32 or bool
+  vec3(vec3<U>) -> vec3<i32>  where: T is i32, U is f32, f16, u32 or bool
+  vec3(vec3<U>) -> vec3<u32>  where: T is u32, U is f32, f16, i32 or bool
+  vec3(vec3<U>) -> vec3<bool>  where: T is bool, U is f32, f16, i32 or u32
 )");
 }
 
diff --git a/src/tint/resolver/is_host_shareable_test.cc b/src/tint/resolver/is_host_shareable_test.cc
index a167903..0ac3352 100644
--- a/src/tint/resolver/is_host_shareable_test.cc
+++ b/src/tint/resolver/is_host_shareable_test.cc
@@ -35,6 +35,7 @@
     EXPECT_TRUE(r()->IsHostShareable(create<sem::I32>()));
     EXPECT_TRUE(r()->IsHostShareable(create<sem::U32>()));
     EXPECT_TRUE(r()->IsHostShareable(create<sem::F32>()));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::F16>()));
 }
 
 TEST_F(ResolverIsHostShareable, NumericVector) {
@@ -47,6 +48,9 @@
     EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 2u)));
     EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 3u)));
     EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 4u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F16>(), 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F16>(), 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F16>(), 4u)));
 }
 
 TEST_F(ResolverIsHostShareable, BoolVector) {
@@ -62,19 +66,32 @@
 }
 
 TEST_F(ResolverIsHostShareable, Matrix) {
-    auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
-    auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
-    auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
+    auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
+    auto* vec3_f32 = create<sem::Vector>(create<sem::F32>(), 3u);
+    auto* vec4_f32 = create<sem::Vector>(create<sem::F32>(), 4u);
+    auto* vec2_f16 = create<sem::Vector>(create<sem::F16>(), 2u);
+    auto* vec3_f16 = create<sem::Vector>(create<sem::F16>(), 3u);
+    auto* vec4_f16 = create<sem::Vector>(create<sem::F16>(), 4u);
 
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 2u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 3u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 4u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 2u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 3u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 4u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 2u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 3u)));
-    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 4u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2_f32, 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2_f32, 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2_f32, 4u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3_f32, 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3_f32, 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3_f32, 4u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4_f32, 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4_f32, 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4_f32, 4u)));
+
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2_f16, 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2_f16, 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2_f16, 4u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3_f16, 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3_f16, 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3_f16, 4u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4_f16, 2u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4_f16, 3u)));
+    EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4_f16, 4u)));
 }
 
 TEST_F(ResolverIsHostShareable, Pointer) {
diff --git a/src/tint/resolver/is_storeable_test.cc b/src/tint/resolver/is_storeable_test.cc
index 2d37d8b..80c3e20 100644
--- a/src/tint/resolver/is_storeable_test.cc
+++ b/src/tint/resolver/is_storeable_test.cc
@@ -32,6 +32,7 @@
     EXPECT_TRUE(r()->IsStorable(create<sem::I32>()));
     EXPECT_TRUE(r()->IsStorable(create<sem::U32>()));
     EXPECT_TRUE(r()->IsStorable(create<sem::F32>()));
+    EXPECT_TRUE(r()->IsStorable(create<sem::F16>()));
 }
 
 TEST_F(ResolverIsStorableTest, Vector) {
@@ -44,21 +45,36 @@
     EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 2u)));
     EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 3u)));
     EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 4u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F16>(), 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F16>(), 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F16>(), 4u)));
 }
 
 TEST_F(ResolverIsStorableTest, Matrix) {
-    auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
-    auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
-    auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 2u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 3u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 4u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 2u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 3u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 4u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 2u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 3u)));
-    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 4u)));
+    auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
+    auto* vec3_f32 = create<sem::Vector>(create<sem::F32>(), 3u);
+    auto* vec4_f32 = create<sem::Vector>(create<sem::F32>(), 4u);
+    auto* vec2_f16 = create<sem::Vector>(create<sem::F16>(), 2u);
+    auto* vec3_f16 = create<sem::Vector>(create<sem::F16>(), 3u);
+    auto* vec4_f16 = create<sem::Vector>(create<sem::F16>(), 4u);
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2_f32, 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2_f32, 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2_f32, 4u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3_f32, 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3_f32, 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3_f32, 4u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4_f32, 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4_f32, 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4_f32, 4u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2_f16, 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2_f16, 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2_f16, 4u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3_f16, 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3_f16, 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3_f16, 4u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4_f16, 2u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4_f16, 3u)));
+    EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4_f16, 4u)));
 }
 
 TEST_F(ResolverIsStorableTest, Pointer) {
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index 22d5606..1b935ee 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -33,6 +33,7 @@
 using i32V = builder::vec<3, i32>;
 using u32V = builder::vec<3, u32>;
 using f32M = builder::mat<3, 2, f32>;
+using i32Varr = builder::array<3, i32>;
 
 constexpr double kHighestU32 = static_cast<double>(u32::kHighest);
 constexpr double kLowestU32 = static_cast<double>(u32::kLowest);
@@ -40,11 +41,16 @@
 constexpr double kLowestI32 = static_cast<double>(i32::kLowest);
 constexpr double kHighestF32 = static_cast<double>(f32::kHighest);
 constexpr double kLowestF32 = static_cast<double>(f32::kLowest);
+// constexpr double kHighestF16 = static_cast<double>(f16::kHighest);
+// constexpr double kLowestF16 = static_cast<double>(f16::kLowest);
 constexpr double kTooBigF32 = static_cast<double>(3.5e+38);
+// constexpr double kTooBigF16 = static_cast<double>(6.6e+4);
 constexpr double kPiF64 = 3.141592653589793;
 constexpr double kPiF32 = 3.1415927410125732;  // kPiF64 quantized to f32
+// constexpr double kPiF16 = 3.140625;         // kPiF64 quantized to f16
 
 constexpr double kSubnormalF32 = 0x1.0p-128;
+// constexpr double kSubnormalF16 = 0x1.0p-16;
 
 enum class Expectation {
     kMaterialize,
@@ -67,12 +73,61 @@
     return o << "<unknown>";
 }
 
+template <typename CASE>
+class MaterializeTest : public resolver::ResolverTestWithParam<CASE> {
+  protected:
+    using ProgramBuilder::FriendlyName;
+
+    void CheckTypesAndValues(const sem::Expression* expr,
+                             const tint::sem::Type* expected_sem_ty,
+                             const std::variant<AInt, AFloat>& expected_value) {
+        std::visit([&](auto v) { CheckTypesAndValuesImpl(expr, expected_sem_ty, v); },
+                   expected_value);
+    }
+
+  private:
+    template <typename T>
+    void CheckTypesAndValuesImpl(const sem::Expression* expr,
+                                 const tint::sem::Type* expected_sem_ty,
+                                 T expected_value) {
+        EXPECT_TYPE(expr->Type(), expected_sem_ty);
+
+        auto* value = expr->ConstantValue();
+        ASSERT_NE(value, nullptr);
+        EXPECT_TYPE(expr->Type(), value->Type());
+
+        tint::Switch(
+            expected_sem_ty,  //
+            [&](const sem::Vector* v) {
+                for (uint32_t i = 0; i < v->Width(); i++) {
+                    auto* el = value->Index(i);
+                    ASSERT_NE(el, nullptr);
+                    EXPECT_TYPE(el->Type(), v->type());
+                    EXPECT_EQ(std::get<T>(el->Value()), expected_value);
+                }
+            },
+            [&](const sem::Matrix* m) {
+                for (uint32_t c = 0; c < m->columns(); c++) {
+                    auto* column = value->Index(c);
+                    ASSERT_NE(column, nullptr);
+                    EXPECT_TYPE(column->Type(), m->ColumnType());
+                    for (uint32_t r = 0; r < m->rows(); r++) {
+                        auto* el = column->Index(r);
+                        ASSERT_NE(el, nullptr);
+                        EXPECT_TYPE(el->Type(), m->type());
+                        EXPECT_EQ(std::get<T>(el->Value()), expected_value);
+                    }
+                }
+            },
+            [&](Default) { EXPECT_EQ(std::get<T>(value->Value()), expected_value); });
+    }
+};
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // MaterializeAbstractNumericToConcreteType
 // Tests that an abstract-numeric will materialize to the expected concrete type
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 namespace materialize_abstract_numeric_to_concrete_type {
-
 // How should the materialization occur?
 enum class Method {
     // var a : target_type = abstract_expr;
@@ -241,10 +296,10 @@
 }
 
 using MaterializeAbstractNumericToConcreteType =
-    resolver::ResolverTestWithParam<std::tuple<Expectation, Method, Data>>;
+    MaterializeTest<std::tuple<Expectation, Method, Data>>;
 
 TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
-    // Once F16 is properly supported, we'll need to enable this:
+    // Once built-in and ops using f16 is properly supported, we'll need to enable this:
     // Enable(ast::Extension::kF16);
 
     const auto& param = GetParam();
@@ -317,30 +372,12 @@
             break;
     }
 
-    auto check_types_and_values = [&](const sem::Expression* expr) {
-        auto* target_sem_ty = data.target_sem_ty(*this);
-
-        EXPECT_TYPE(expr->Type(), target_sem_ty);
-        EXPECT_TYPE(expr->ConstantValue().Type(), target_sem_ty);
-
-        uint32_t num_elems = 0;
-        const sem::Type* target_sem_el_ty = sem::Type::ElementOf(target_sem_ty, &num_elems);
-        EXPECT_TYPE(expr->ConstantValue().ElementType(), target_sem_el_ty);
-        expr->ConstantValue().WithElements([&](auto&& vec) {
-            using VEC_TY = std::decay_t<decltype(vec)>;
-            using EL_TY = typename VEC_TY::value_type;
-            ASSERT_TRUE(std::holds_alternative<EL_TY>(data.materialized_value));
-            VEC_TY expected(num_elems, std::get<EL_TY>(data.materialized_value));
-            EXPECT_EQ(vec, expected);
-        });
-    };
-
     switch (expectation) {
         case Expectation::kMaterialize: {
             ASSERT_TRUE(r()->Resolve()) << r()->error();
             auto* materialize = Sem().Get<sem::Materialize>(abstract_expr);
             ASSERT_NE(materialize, nullptr);
-            check_types_and_values(materialize);
+            CheckTypesAndValues(materialize, data.target_sem_ty(*this), data.materialized_value);
             break;
         }
         case Expectation::kNoMaterialize: {
@@ -348,7 +385,7 @@
             auto* sem = Sem().Get(abstract_expr);
             ASSERT_NE(sem, nullptr);
             EXPECT_FALSE(sem->Is<sem::Materialize>());
-            check_types_and_values(sem);
+            CheckTypesAndValues(sem, data.target_sem_ty(*this), data.materialized_value);
             break;
         }
         case Expectation::kInvalidConversion: {
@@ -408,8 +445,8 @@
 /// Methods that do not materialize
 constexpr Method kNoMaterializeMethods[] = {
     Method::kPhonyAssign,
-    // TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops:
-    // Method::kBuiltinArg, Method::kBinaryOp,
+    // TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary
+    // ops: Method::kBuiltinArg, Method::kBinaryOp,
 };
 INSTANTIATE_TEST_SUITE_P(
     MaterializeScalar,
@@ -417,23 +454,28 @@
     testing::Combine(testing::Values(Expectation::kMaterialize),
                      testing::ValuesIn(kScalarMethods),
                      testing::ValuesIn(std::vector<Data>{
-                         Types<i32, AInt>(0_a, 0.0),                                  //
-                         Types<i32, AInt>(1_a, 1.0),                                  //
-                         Types<i32, AInt>(-1_a, -1.0),                                //
-                         Types<i32, AInt>(AInt(kHighestI32), kHighestI32),            //
-                         Types<i32, AInt>(AInt(kLowestI32), kLowestI32),              //
-                         Types<u32, AInt>(0_a, 0.0),                                  //
-                         Types<u32, AInt>(1_a, 1.0),                                  //
-                         Types<u32, AInt>(AInt(kHighestU32), kHighestU32),            //
-                         Types<u32, AInt>(AInt(kLowestU32), kLowestU32),              //
-                         Types<f32, AFloat>(0.0_a, 0.0),                              //
-                         Types<f32, AFloat>(AFloat(kHighestF32), kHighestF32),        //
-                         Types<f32, AFloat>(AFloat(kLowestF32), kLowestF32),          //
-                         Types<f32, AFloat>(AFloat(kPiF32), kPiF64),                  //
-                         Types<f32, AFloat>(AFloat(kSubnormalF32), kSubnormalF32),    //
-                         Types<f32, AFloat>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
-                         /* Types<f16, AFloat>(1.0_a), */                             //
-                         /* Types<f16, AFloat>(1.0_a), */                             //
+                         Types<i32, AInt>(0_a, 0.0),                                        //
+                         Types<i32, AInt>(1_a, 1.0),                                        //
+                         Types<i32, AInt>(-1_a, -1.0),                                      //
+                         Types<i32, AInt>(AInt(kHighestI32), kHighestI32),                  //
+                         Types<i32, AInt>(AInt(kLowestI32), kLowestI32),                    //
+                         Types<u32, AInt>(0_a, 0.0),                                        //
+                         Types<u32, AInt>(1_a, 1.0),                                        //
+                         Types<u32, AInt>(AInt(kHighestU32), kHighestU32),                  //
+                         Types<u32, AInt>(AInt(kLowestU32), kLowestU32),                    //
+                         Types<f32, AFloat>(0.0_a, 0.0),                                    //
+                         Types<f32, AFloat>(AFloat(kHighestF32), kHighestF32),              //
+                         Types<f32, AFloat>(AFloat(kLowestF32), kLowestF32),                //
+                         Types<f32, AFloat>(AFloat(kPiF32), kPiF64),                        //
+                         Types<f32, AFloat>(AFloat(kSubnormalF32), kSubnormalF32),          //
+                         Types<f32, AFloat>(AFloat(-kSubnormalF32), -kSubnormalF32),        //
+                         /* Types<f16, AFloat>(0.0_a, 0.0),                             */  //
+                         /* Types<f16, AFloat>(1.0_a, 1.0),                             */  //
+                         /* Types<f16, AFloat>(AFloat(kHighestF16), kHighestF16),       */  //
+                         /* Types<f16, AFloat>(AFloat(kLowestF16), kLowestF16),         */  //
+                         /* Types<f16, AFloat>(AFloat(kPiF16), kPiF64),                 */  //
+                         /* Types<f16, AFloat>(AFloat(kSubnormalF16), kSubnormalF16),   */  //
+                         /* Types<f16, AFloat>(AFloat(-kSubnormalF16), -kSubnormalF16), */  //
                      })));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -442,25 +484,31 @@
     testing::Combine(testing::Values(Expectation::kMaterialize),
                      testing::ValuesIn(kVectorMethods),
                      testing::ValuesIn(std::vector<Data>{
-                         Types<i32V, AIntV>(0_a, 0.0),                                  //
-                         Types<i32V, AIntV>(1_a, 1.0),                                  //
-                         Types<i32V, AIntV>(-1_a, -1.0),                                //
-                         Types<i32V, AIntV>(AInt(kHighestI32), kHighestI32),            //
-                         Types<i32V, AIntV>(AInt(kLowestI32), kLowestI32),              //
-                         Types<u32V, AIntV>(0_a, 0.0),                                  //
-                         Types<u32V, AIntV>(1_a, 1.0),                                  //
-                         Types<u32V, AIntV>(AInt(kHighestU32), kHighestU32),            //
-                         Types<u32V, AIntV>(AInt(kLowestU32), kLowestU32),              //
-                         Types<f32V, AFloatV>(0.0_a, 0.0),                              //
-                         Types<f32V, AFloatV>(1.0_a, 1.0),                              //
-                         Types<f32V, AFloatV>(-1.0_a, -1.0),                            //
-                         Types<f32V, AFloatV>(AFloat(kHighestF32), kHighestF32),        //
-                         Types<f32V, AFloatV>(AFloat(kLowestF32), kLowestF32),          //
-                         Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64),                  //
-                         Types<f32V, AFloatV>(AFloat(kSubnormalF32), kSubnormalF32),    //
-                         Types<f32V, AFloatV>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
-                         /* Types<f16V, AFloatV>(1.0_a), */                             //
-                         /* Types<f16V, AFloatV>(1.0_a), */                             //
+                         Types<i32V, AIntV>(0_a, 0.0),                                        //
+                         Types<i32V, AIntV>(1_a, 1.0),                                        //
+                         Types<i32V, AIntV>(-1_a, -1.0),                                      //
+                         Types<i32V, AIntV>(AInt(kHighestI32), kHighestI32),                  //
+                         Types<i32V, AIntV>(AInt(kLowestI32), kLowestI32),                    //
+                         Types<u32V, AIntV>(0_a, 0.0),                                        //
+                         Types<u32V, AIntV>(1_a, 1.0),                                        //
+                         Types<u32V, AIntV>(AInt(kHighestU32), kHighestU32),                  //
+                         Types<u32V, AIntV>(AInt(kLowestU32), kLowestU32),                    //
+                         Types<f32V, AFloatV>(0.0_a, 0.0),                                    //
+                         Types<f32V, AFloatV>(1.0_a, 1.0),                                    //
+                         Types<f32V, AFloatV>(-1.0_a, -1.0),                                  //
+                         Types<f32V, AFloatV>(AFloat(kHighestF32), kHighestF32),              //
+                         Types<f32V, AFloatV>(AFloat(kLowestF32), kLowestF32),                //
+                         Types<f32V, AFloatV>(AFloat(kPiF32), kPiF64),                        //
+                         Types<f32V, AFloatV>(AFloat(kSubnormalF32), kSubnormalF32),          //
+                         Types<f32V, AFloatV>(AFloat(-kSubnormalF32), -kSubnormalF32),        //
+                         /* Types<f16V, AFloatV>(0.0_a, 0.0),                             */  //
+                         /* Types<f16V, AFloatV>(1.0_a, 1.0),                             */  //
+                         /* Types<f16V, AFloatV>(-1.0_a, -1.0),                           */  //
+                         /* Types<f16V, AFloatV>(AFloat(kHighestF16), kHighestF16),       */  //
+                         /* Types<f16V, AFloatV>(AFloat(kLowestF16), kLowestF16),         */  //
+                         /* Types<f16V, AFloatV>(AFloat(kPiF16), kPiF64),                 */  //
+                         /* Types<f16V, AFloatV>(AFloat(kSubnormalF16), kSubnormalF16),   */  //
+                         /* Types<f16V, AFloatV>(AFloat(-kSubnormalF16), -kSubnormalF16), */  //
                      })));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -469,15 +517,22 @@
     testing::Combine(testing::Values(Expectation::kMaterialize),
                      testing::ValuesIn(kMatrixMethods),
                      testing::ValuesIn(std::vector<Data>{
-                         Types<f32M, AFloatM>(0.0_a, 0.0),                              //
-                         Types<f32M, AFloatM>(1.0_a, 1.0),                              //
-                         Types<f32M, AFloatM>(-1.0_a, -1.0),                            //
-                         Types<f32M, AFloatM>(AFloat(kHighestF32), kHighestF32),        //
-                         Types<f32M, AFloatM>(AFloat(kLowestF32), kLowestF32),          //
-                         Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64),                  //
-                         Types<f32M, AFloatM>(AFloat(kSubnormalF32), kSubnormalF32),    //
-                         Types<f32M, AFloatM>(AFloat(-kSubnormalF32), -kSubnormalF32),  //
-                         /* Types<f16V, AFloatM>(1.0_a), */                             //
+                         Types<f32M, AFloatM>(0.0_a, 0.0),                                    //
+                         Types<f32M, AFloatM>(1.0_a, 1.0),                                    //
+                         Types<f32M, AFloatM>(-1.0_a, -1.0),                                  //
+                         Types<f32M, AFloatM>(AFloat(kHighestF32), kHighestF32),              //
+                         Types<f32M, AFloatM>(AFloat(kLowestF32), kLowestF32),                //
+                         Types<f32M, AFloatM>(AFloat(kPiF32), kPiF64),                        //
+                         Types<f32M, AFloatM>(AFloat(kSubnormalF32), kSubnormalF32),          //
+                         Types<f32M, AFloatM>(AFloat(-kSubnormalF32), -kSubnormalF32),        //
+                         /* Types<f16M, AFloatM>(0.0_a, 0.0),                             */  //
+                         /* Types<f16M, AFloatM>(1.0_a, 1.0),                             */  //
+                         /* Types<f16M, AFloatM>(-1.0_a, -1.0),                           */  //
+                         /* Types<f16M, AFloatM>(AFloat(kHighestF16), kHighestF16),       */  //
+                         /* Types<f16M, AFloatM>(AFloat(kLowestF16), kLowestF16),         */  //
+                         /* Types<f16M, AFloatM>(AFloat(kPiF16), kPiF64),                 */  //
+                         /* Types<f16M, AFloatM>(AFloat(kSubnormalF16), kSubnormalF16),   */  //
+                         /* Types<f16M, AFloatM>(AFloat(-kSubnormalF16), -kSubnormalF16), */  //
                      })));
 
 INSTANTIATE_TEST_SUITE_P(MaterializeSwitch,
@@ -526,10 +581,14 @@
                          testing::Combine(testing::Values(Expectation::kInvalidConversion),
                                           testing::ValuesIn(kScalarMethods),
                                           testing::ValuesIn(std::vector<Data>{
-                                              Types<i32, AFloat>(),    //
-                                              Types<u32, AFloat>(),    //
-                                              Types<i32V, AFloatV>(),  //
-                                              Types<u32V, AFloatV>(),  //
+                                              Types<i32, AFloat>(),       //
+                                              Types<u32, AFloat>(),       //
+                                              Types<i32V, AFloatV>(),     //
+                                              Types<u32V, AFloatV>(),     //
+                                              Types<i32Varr, AInt>(),     //
+                                              Types<i32Varr, AIntV>(),    //
+                                              Types<i32Varr, AFloat>(),   //
+                                              Types<i32Varr, AFloatV>(),  //
                                           })));
 
 INSTANTIATE_TEST_SUITE_P(ScalarValueCannotBeRepresented,
@@ -537,14 +596,14 @@
                          testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
                                           testing::ValuesIn(kScalarMethods),
                                           testing::ValuesIn(std::vector<Data>{
-                                              Types<i32, AInt>(0_a, kHighestI32 + 1),  //
-                                              Types<i32, AInt>(0_a, kLowestI32 - 1),   //
-                                              Types<u32, AInt>(0_a, kHighestU32 + 1),  //
-                                              Types<u32, AInt>(0_a, kLowestU32 - 1),   //
-                                              Types<f32, AFloat>(0.0_a, kTooBigF32),   //
-                                              Types<f32, AFloat>(0.0_a, -kTooBigF32),  //
-                                              /* Types<f16, AFloat>(), */              //
-                                              /* Types<f16, AFloat>(), */              //
+                                              Types<i32, AInt>(0_a, kHighestI32 + 1),        //
+                                              Types<i32, AInt>(0_a, kLowestI32 - 1),         //
+                                              Types<u32, AInt>(0_a, kHighestU32 + 1),        //
+                                              Types<u32, AInt>(0_a, kLowestU32 - 1),         //
+                                              Types<f32, AFloat>(0.0_a, kTooBigF32),         //
+                                              Types<f32, AFloat>(0.0_a, -kTooBigF32),        //
+                                              /* Types<f16, AFloat>(0.0_a, kTooBigF16),  */  //
+                                              /* Types<f16, AFloat>(0.0_a, -kTooBigF16), */  //
                                           })));
 
 INSTANTIATE_TEST_SUITE_P(VectorValueCannotBeRepresented,
@@ -552,14 +611,14 @@
                          testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
                                           testing::ValuesIn(kVectorMethods),
                                           testing::ValuesIn(std::vector<Data>{
-                                              Types<i32V, AIntV>(0_a, kHighestI32 + 1),  //
-                                              Types<i32V, AIntV>(0_a, kLowestI32 - 1),   //
-                                              Types<u32V, AIntV>(0_a, kHighestU32 + 1),  //
-                                              Types<u32V, AIntV>(0_a, kLowestU32 - 1),   //
-                                              Types<f32V, AFloatV>(0.0_a, kTooBigF32),   //
-                                              Types<f32V, AFloatV>(0.0_a, -kTooBigF32),  //
-                                              /* Types<f16V, AFloatV>(), */              //
-                                              /* Types<f16V, AFloatV>(), */              //
+                                              Types<i32V, AIntV>(0_a, kHighestI32 + 1),        //
+                                              Types<i32V, AIntV>(0_a, kLowestI32 - 1),         //
+                                              Types<u32V, AIntV>(0_a, kHighestU32 + 1),        //
+                                              Types<u32V, AIntV>(0_a, kLowestU32 - 1),         //
+                                              Types<f32V, AFloatV>(0.0_a, kTooBigF32),         //
+                                              Types<f32V, AFloatV>(0.0_a, -kTooBigF32),        //
+                                              /* Types<f16V, AFloatV>(0.0_a, kTooBigF16),  */  //
+                                              /* Types<f16V, AFloatV>(0.0_a, -kTooBigF16), */  //
                                           })));
 
 INSTANTIATE_TEST_SUITE_P(MatrixValueCannotBeRepresented,
@@ -567,10 +626,10 @@
                          testing::Combine(testing::Values(Expectation::kValueCannotBeRepresented),
                                           testing::ValuesIn(kMatrixMethods),
                                           testing::ValuesIn(std::vector<Data>{
-                                              Types<f32M, AFloatM>(0.0_a, kTooBigF32),   //
-                                              Types<f32M, AFloatM>(0.0_a, -kTooBigF32),  //
-                                              /* Types<f16M, AFloatM>(), */              //
-                                              /* Types<f16M, AFloatM>(), */              //
+                                              Types<f32M, AFloatM>(0.0_a, kTooBigF32),         //
+                                              Types<f32M, AFloatM>(0.0_a, -kTooBigF32),        //
+                                              /* Types<f16M, AFloatM>(0.0_a, kTooBigF16),  */  //
+                                              /* Types<f16M, AFloatM>(0.0_a, -kTooBigF16), */  //
                                           })));
 
 }  // namespace materialize_abstract_numeric_to_concrete_type
@@ -675,12 +734,9 @@
 }
 
 using MaterializeAbstractNumericToDefaultType =
-    resolver::ResolverTestWithParam<std::tuple<Expectation, Method, Data>>;
+    MaterializeTest<std::tuple<Expectation, Method, Data>>;
 
 TEST_P(MaterializeAbstractNumericToDefaultType, Test) {
-    // Once F16 is properly supported, we'll need to enable this:
-    // Enable(ast::Extension::kF16);
-
     const auto& param = GetParam();
     const auto& expectation = std::get<0>(param);
     const auto& method = std::get<1>(param);
@@ -721,36 +777,19 @@
                  {WorkgroupSize(abstract_expr()), Stage(ast::PipelineStage::kCompute)});
             break;
         case Method::kIndex:
-            Global("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
+            GlobalVar("arr", ty.array<i32, 4>(), ast::StorageClass::kPrivate);
             WrapInFunction(IndexAccessor("arr", abstract_expr()));
             break;
     }
 
-    auto check_types_and_values = [&](const sem::Expression* expr) {
-        auto* expected_sem_ty = data.expected_sem_ty(*this);
-
-        EXPECT_TYPE(expr->Type(), expected_sem_ty);
-        EXPECT_TYPE(expr->ConstantValue().Type(), expected_sem_ty);
-
-        uint32_t num_elems = 0;
-        const sem::Type* expected_sem_el_ty = sem::Type::ElementOf(expected_sem_ty, &num_elems);
-        EXPECT_TYPE(expr->ConstantValue().ElementType(), expected_sem_el_ty);
-        expr->ConstantValue().WithElements([&](auto&& vec) {
-            using VEC_TY = std::decay_t<decltype(vec)>;
-            using EL_TY = typename VEC_TY::value_type;
-            ASSERT_TRUE(std::holds_alternative<EL_TY>(data.materialized_value));
-            VEC_TY expected(num_elems, std::get<EL_TY>(data.materialized_value));
-            EXPECT_EQ(vec, expected);
-        });
-    };
-
     switch (expectation) {
         case Expectation::kMaterialize: {
             ASSERT_TRUE(r()->Resolve()) << r()->error();
             for (auto* expr : abstract_exprs) {
                 auto* materialize = Sem().Get<sem::Materialize>(expr);
                 ASSERT_NE(materialize, nullptr);
-                check_types_and_values(materialize);
+                CheckTypesAndValues(materialize, data.expected_sem_ty(*this),
+                                    data.materialized_value);
             }
             break;
         }
@@ -959,5 +998,25 @@
 
 }  // namespace materialize_abstract_numeric_to_default_type
 
+using MaterializeAbstractNumericToUnrelatedType = resolver::ResolverTest;
+
+TEST_F(MaterializeAbstractNumericToUnrelatedType, AIntToStructVarCtor) {
+    Structure("S", {Member("a", ty.i32())});
+    WrapInFunction(Decl(Var("v", ty.type_name("S"), Expr(Source{{12, 34}}, 1_a))));
+    ASSERT_FALSE(r()->Resolve());
+    EXPECT_THAT(
+        r()->error(),
+        testing::HasSubstr("error: cannot convert value of type 'abstract-int' to type 'S'"));
+}
+
+TEST_F(MaterializeAbstractNumericToUnrelatedType, AIntToStructLetCtor) {
+    Structure("S", {Member("a", ty.i32())});
+    WrapInFunction(Decl(Let("v", ty.type_name("S"), Expr(Source{{12, 34}}, 1_a))));
+    ASSERT_FALSE(r()->Resolve());
+    EXPECT_THAT(
+        r()->error(),
+        testing::HasSubstr("error: cannot convert value of type 'abstract-int' to type 'S'"));
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/ptr_ref_test.cc b/src/tint/resolver/ptr_ref_test.cc
index 69b7e54..037c6ff 100644
--- a/src/tint/resolver/ptr_ref_test.cc
+++ b/src/tint/resolver/ptr_ref_test.cc
@@ -59,18 +59,18 @@
 
     auto* buf = Structure("S", {Member("m", ty.i32())});
     auto* function = Var("f", ty.i32());
-    auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
-    auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
-    auto* uniform = Global("ub", ty.Of(buf), ast::StorageClass::kUniform,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(0),
-                               create<ast::GroupAttribute>(0),
-                           });
-    auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(1),
-                               create<ast::GroupAttribute>(0),
-                           });
+    auto* private_ = GlobalVar("p", ty.i32(), ast::StorageClass::kPrivate);
+    auto* workgroup = GlobalVar("w", ty.i32(), ast::StorageClass::kWorkgroup);
+    auto* uniform = GlobalVar("ub", ty.Of(buf), ast::StorageClass::kUniform,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(0u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
+    auto* storage = GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(1u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
 
     auto* function_ptr =
         Let("f_ptr", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(function));
diff --git a/src/tint/resolver/ptr_ref_validation_test.cc b/src/tint/resolver/ptr_ref_validation_test.cc
index b011dd0..768783b 100644
--- a/src/tint/resolver/ptr_ref_validation_test.cc
+++ b/src/tint/resolver/ptr_ref_validation_test.cc
@@ -54,7 +54,8 @@
 TEST_F(ResolverPtrRefValidationTest, AddressOfHandle) {
     // @group(0) @binding(0) var t: texture_3d<f32>;
     // &t
-    Global("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()), GroupAndBinding(0u, 0u));
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()),
+              GroupAndBinding(0u, 0u));
     auto* expr = AddressOf(Expr(Source{{12, 34}}, "t"));
     WrapInFunction(expr);
 
@@ -93,7 +94,8 @@
 TEST_F(ResolverPtrRefValidationTest, IndirectOfAddressOfHandle) {
     // @group(0) @binding(0) var t: texture_3d<f32>;
     // *&t
-    Global("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()), GroupAndBinding(0u, 0u));
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()),
+              GroupAndBinding(0u, 0u));
     auto* expr = Deref(AddressOf(Expr(Source{{12, 34}}, "t")));
     WrapInFunction(expr);
 
@@ -141,11 +143,11 @@
     // }
     auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
     auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
-    auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(0),
-                               create<ast::GroupAttribute>(0),
-                           });
+    auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(0u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
 
     auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 2_i);
     auto* ptr =
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index be1765c..9ef9a30 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -62,6 +62,7 @@
 #include "src/tint/sem/for_loop_statement.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/if_statement.h"
+#include "src/tint/sem/index_accessor_expression.h"
 #include "src/tint/sem/loop_statement.h"
 #include "src/tint/sem/materialize.h"
 #include "src/tint/sem/member_accessor_expression.h"
@@ -312,57 +313,214 @@
 }
 
 sem::Variable* Resolver::Variable(const ast::Variable* v, bool is_global) {
+    return Switch(
+        v,  //
+        [&](const ast::Var* var) { return Var(var, is_global); },
+        [&](const ast::Let* let) { return Let(let, is_global); },
+        [&](const ast::Override* override) { return Override(override); },
+        [&](const ast::Const* const_) { return Const(const_, is_global); },
+        [&](Default) {
+            TINT_ICE(Resolver, diagnostics_)
+                << "Resolver::GlobalVariable() called with a unknown variable type: "
+                << v->TypeInfo().name;
+            return nullptr;
+        });
+}
+
+sem::Variable* Resolver::Let(const ast::Let* v, bool is_global) {
+    const sem::Type* ty = nullptr;
+
+    // If the variable has a declared type, resolve it.
+    if (v->type) {
+        ty = Type(v->type);
+        if (!ty) {
+            return nullptr;
+        }
+    }
+
+    if (!v->constructor) {
+        AddError("'let' declaration must have an initializer", v->source);
+        return nullptr;
+    }
+
+    auto* rhs = Materialize(Expression(v->constructor), ty);
+    if (!rhs) {
+        return nullptr;
+    }
+
+    // If the variable has no declared type, infer it from the RHS
+    if (!ty) {
+        ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
+    }
+
+    if (rhs && !validator_.VariableInitializer(v, ast::StorageClass::kNone, ty, rhs)) {
+        return nullptr;
+    }
+
+    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+                                      v->source)) {
+        AddNote("while instantiating 'let' " + builder_->Symbols().NameFor(v->symbol), v->source);
+        return nullptr;
+    }
+
+    sem::Variable* sem = nullptr;
+    if (is_global) {
+        sem = builder_->create<sem::GlobalVariable>(
+            v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr,
+            sem::BindingPoint{});
+    } else {
+        sem = builder_->create<sem::LocalVariable>(v, ty, ast::StorageClass::kNone,
+                                                   ast::Access::kUndefined, current_statement_,
+                                                   /* constant_value */ nullptr);
+    }
+
+    sem->SetConstructor(rhs);
+    builder_->Sem().Add(v, sem);
+    return sem;
+}
+
+sem::Variable* Resolver::Override(const ast::Override* v) {
+    const sem::Type* ty = nullptr;
+
+    // If the variable has a declared type, resolve it.
+    if (v->type) {
+        ty = Type(v->type);
+        if (!ty) {
+            return nullptr;
+        }
+    }
+
+    const sem::Expression* rhs = nullptr;
+
+    // Does the variable have a constructor?
+    if (v->constructor) {
+        rhs = Materialize(Expression(v->constructor), ty);
+        if (!rhs) {
+            return nullptr;
+        }
+
+        // If the variable has no declared type, infer it from the RHS
+        if (!ty) {
+            ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
+        }
+    } else if (!ty) {
+        AddError("override declaration requires a type or initializer", v->source);
+        return nullptr;
+    }
+
+    if (rhs && !validator_.VariableInitializer(v, ast::StorageClass::kNone, ty, rhs)) {
+        return nullptr;
+    }
+
+    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+                                      v->source)) {
+        AddNote("while instantiating 'override' " + builder_->Symbols().NameFor(v->symbol),
+                v->source);
+        return nullptr;
+    }
+
+    auto* sem = builder_->create<sem::GlobalVariable>(
+        v, ty, ast::StorageClass::kNone, ast::Access::kUndefined, /* constant_value */ nullptr,
+        sem::BindingPoint{});
+
+    if (auto* id = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
+        sem->SetConstantId(static_cast<uint16_t>(id->value));
+    }
+
+    sem->SetConstructor(rhs);
+    builder_->Sem().Add(v, sem);
+    return sem;
+}
+
+sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
+    const sem::Type* ty = nullptr;
+
+    // If the variable has a declared type, resolve it.
+    if (c->type) {
+        ty = Type(c->type);
+        if (!ty) {
+            return nullptr;
+        }
+    }
+
+    if (!c->constructor) {
+        AddError("'const' declaration must have an initializer", c->source);
+        return nullptr;
+    }
+
+    const auto* rhs = Expression(c->constructor);
+    if (!rhs) {
+        return nullptr;
+    }
+
+    if (ty) {
+        // If an explicit type was specified, materialize to that type
+        rhs = Materialize(rhs, ty);
+    } else {
+        // If no type was specified, infer it from the RHS
+        ty = rhs->Type();
+    }
+
+    const auto value = rhs->ConstantValue();
+    if (!value) {
+        AddError("'const' initializer must be constant expression", c->constructor->source);
+        return nullptr;
+    }
+
+    if (!validator_.VariableInitializer(c, ast::StorageClass::kNone, ty, rhs)) {
+        return nullptr;
+    }
+
+    if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, const_cast<sem::Type*>(ty),
+                                      c->source)) {
+        AddNote("while instantiating 'const' " + builder_->Symbols().NameFor(c->symbol), c->source);
+        return nullptr;
+    }
+
+    auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
+                                c, ty, ast::StorageClass::kNone, ast::Access::kUndefined, value,
+                                sem::BindingPoint{}))
+                          : static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
+                                c, ty, ast::StorageClass::kNone, ast::Access::kUndefined,
+                                current_statement_, value));
+
+    sem->SetConstructor(rhs);
+    builder_->Sem().Add(c, sem);
+    return sem;
+}
+
+sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
     const sem::Type* storage_ty = nullptr;
 
     // If the variable has a declared type, resolve it.
-    if (auto* ty = v->type) {
+    if (auto* ty = var->type) {
         storage_ty = Type(ty);
         if (!storage_ty) {
             return nullptr;
         }
     }
 
-    auto* as_var = v->As<ast::Var>();
-    auto* as_let = v->As<ast::Let>();
-    auto* as_override = v->As<ast::Override>();
-
     const sem::Expression* rhs = nullptr;
 
     // Does the variable have a constructor?
-    if (v->constructor) {
-        rhs = Materialize(Expression(v->constructor), storage_ty);
+    if (var->constructor) {
+        rhs = Materialize(Expression(var->constructor), storage_ty);
         if (!rhs) {
             return nullptr;
         }
-
         // If the variable has no declared type, infer it from the RHS
         if (!storage_ty) {
-            if (as_var && is_global) {
-                AddError("module-scope 'var' declaration must specify a type", v->source);
-                return nullptr;
-            }
-
             storage_ty = rhs->Type()->UnwrapRef();  // Implicit load of RHS
         }
-    } else if (as_let) {
-        AddError("'let' declaration must have an initializer", v->source);
-        return nullptr;
-    } else if (!v->type) {
-        AddError((is_global) ? "module-scope 'var' declaration requires a type or initializer"
-                             : "function-scope 'var' declaration requires a type or initializer",
-                 v->source);
-        return nullptr;
     }
 
     if (!storage_ty) {
-        TINT_ICE(Resolver, diagnostics_) << "failed to determine storage type for variable '" +
-                                                builder_->Symbols().NameFor(v->symbol) + "'\n"
-                                         << "Source: " << v->source;
+        AddError("var declaration requires a type or initializer", var->source);
         return nullptr;
     }
 
-    auto storage_class = as_var ? as_var->declared_storage_class : ast::StorageClass::kNone;
-    if (storage_class == ast::StorageClass::kNone && as_var) {
+    auto storage_class = var->declared_storage_class;
+    if (storage_class == ast::StorageClass::kNone) {
         // No declared storage class. Infer from usage / type.
         if (!is_global) {
             storage_class = ast::StorageClass::kFunction;
@@ -375,65 +533,47 @@
         }
     }
 
-    if (!is_global && as_var && storage_class != ast::StorageClass::kFunction &&
-        validator_.IsValidationEnabled(v->attributes,
+    if (!is_global && storage_class != ast::StorageClass::kFunction &&
+        validator_.IsValidationEnabled(var->attributes,
                                        ast::DisabledValidation::kIgnoreStorageClass)) {
-        AddError("function-scope 'var' declaration must use 'function' storage class", v->source);
+        AddError("function-scope 'var' declaration must use 'function' storage class", var->source);
         return nullptr;
     }
 
-    auto access = as_var ? as_var->declared_access : ast::Access::kUndefined;
+    auto access = var->declared_access;
     if (access == ast::Access::kUndefined) {
         access = DefaultAccessForStorageClass(storage_class);
     }
 
-    auto* var_ty = storage_ty;
-    if (as_var) {
-        // Variable declaration. Unlike `let` and parameters, `var` has storage.
-        // Variables are always of a reference type to the declared storage type.
-        var_ty = builder_->create<sem::Reference>(storage_ty, storage_class, access);
-    }
-
-    if (rhs && !validator_.VariableConstructorOrCast(v, storage_class, storage_ty, rhs->Type())) {
+    if (rhs && !validator_.VariableInitializer(var, storage_class, storage_ty, rhs)) {
         return nullptr;
     }
 
-    if (!ApplyStorageClassUsageToType(storage_class, const_cast<sem::Type*>(var_ty), v->source)) {
-        AddNote("while instantiating variable " + builder_->Symbols().NameFor(v->symbol),
-                v->source);
+    auto* var_ty = builder_->create<sem::Reference>(storage_ty, storage_class, access);
+
+    if (!ApplyStorageClassUsageToType(storage_class, var_ty, var->source)) {
+        AddNote("while instantiating 'var' " + builder_->Symbols().NameFor(var->symbol),
+                var->source);
         return nullptr;
     }
 
+    sem::Variable* sem = nullptr;
     if (is_global) {
         sem::BindingPoint binding_point;
-        if (as_var) {
-            if (auto bp = as_var->BindingPoint()) {
-                binding_point = {bp.group->value, bp.binding->value};
-            }
+        if (auto bp = var->BindingPoint()) {
+            binding_point = {bp.group->value, bp.binding->value};
         }
+        sem = builder_->create<sem::GlobalVariable>(var, var_ty, storage_class, access,
+                                                    /* constant_value */ nullptr, binding_point);
 
-        bool has_const_val = rhs && as_let && !as_override;
-        auto* global = builder_->create<sem::GlobalVariable>(
-            v, var_ty, storage_class, access,
-            has_const_val ? rhs->ConstantValue() : sem::Constant{}, binding_point);
-
-        if (as_override) {
-            if (auto* id = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
-                global->SetConstantId(static_cast<uint16_t>(id->value));
-            }
-        }
-
-        global->SetConstructor(rhs);
-        builder_->Sem().Add(v, global);
-        return global;
+    } else {
+        sem = builder_->create<sem::LocalVariable>(
+            var, var_ty, storage_class, access, current_statement_, /* constant_value */ nullptr);
     }
 
-    auto* local = builder_->create<sem::LocalVariable>(
-        v, var_ty, storage_class, access, current_statement_,
-        (rhs && as_let) ? rhs->ConstantValue() : sem::Constant{});
-    builder_->Sem().Add(v, local);
-    local->SetConstructor(rhs);
-    return local;
+    sem->SetConstructor(rhs);
+    builder_->Sem().Add(var, sem);
+    return sem;
 }
 
 sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index) {
@@ -539,14 +679,6 @@
         return nullptr;
     }
 
-    const bool is_var = v->Is<ast::Var>();
-
-    auto storage_class = sem->StorageClass();
-    if (is_var && storage_class == ast::StorageClass::kNone) {
-        AddError("module-scope 'var' declaration must have a storage class", v->source);
-        return nullptr;
-    }
-
     for (auto* attr : v->attributes) {
         Mark(attr);
 
@@ -570,7 +702,7 @@
         return nullptr;
     }
 
-    return sem->As<sem::GlobalVariable>();
+    return sem;
 }
 
 sem::Function* Resolver::Function(const ast::Function* decl) {
@@ -725,7 +857,7 @@
 bool Resolver::WorkgroupSize(const ast::Function* func) {
     // Set work-group size defaults.
     sem::WorkgroupSize ws;
-    for (int i = 0; i < 3; i++) {
+    for (size_t i = 0; i < 3; i++) {
         ws[i].value = 1;
         ws[i].overridable_const = nullptr;
     }
@@ -740,11 +872,11 @@
     std::array<const sem::Type*, 3> arg_tys = {};
     size_t arg_count = 0;
 
-    constexpr const char* kErrBadType =
-        "workgroup_size argument must be either literal or module-scope constant of type i32 "
-        "or u32";
+    constexpr const char* kErrBadExpr =
+        "workgroup_size argument must be either a literal, constant, or overridable of type "
+        "abstract-integer, i32 or u32";
 
-    for (int i = 0; i < 3; i++) {
+    for (size_t i = 0; i < 3; i++) {
         // Each argument to this attribute can either be a literal, an identifier for a module-scope
         // constants, or nullptr if not specified.
         auto* value = values[i];
@@ -757,7 +889,7 @@
         }
         auto* ty = expr->Type();
         if (!ty->IsAnyOf<sem::I32, sem::U32, sem::AbstractInt>()) {
-            AddError(kErrBadType, value->source);
+            AddError(kErrBadExpr, value->source);
             return false;
         }
 
@@ -784,13 +916,13 @@
             return false;
         }
 
-        sem::Constant value;
+        const sem::Constant* value = nullptr;
 
         if (auto* user = args[i]->As<sem::VariableUser>()) {
             // We have an variable of a module-scope constant.
             auto* decl = user->Variable()->Declaration();
-            if (!decl->IsAnyOf<ast::Let, ast::Override>()) {
-                AddError(kErrBadType, values[i]->source);
+            if (!decl->IsAnyOf<ast::Const, ast::Override>()) {
+                AddError(kErrBadExpr, values[i]->source);
                 return false;
             }
             // Capture the constant if it is pipeline-overridable.
@@ -808,10 +940,7 @@
         } else if (values[i]->Is<ast::LiteralExpression>()) {
             value = materialized->ConstantValue();
         } else {
-            AddError(
-                "workgroup_size argument must be either a literal or a "
-                "module-scope constant",
-                values[i]->source);
+            AddError(kErrBadExpr, values[i]->source);
             return false;
         }
 
@@ -821,12 +950,12 @@
             continue;
         }
         // validator_.Validate and set the default value for this dimension.
-        if (value.Element<AInt>(0).value < 1) {
+        if (value->As<AInt>() < 1) {
             AddError("workgroup_size argument must be at least 1", values[i]->source);
             return false;
         }
 
-        ws[i].value = value.Element<uint32_t>(0);
+        ws[i].value = value->As<uint32_t>();
     }
 
     current_function_->SetWorkgroupSize(std::move(ws));
@@ -1137,7 +1266,8 @@
             [&](const ast::UnaryOpExpression* unary) -> sem::Expression* { return UnaryOp(unary); },
             [&](const ast::PhonyExpression*) -> sem::Expression* {
                 return builder_->create<sem::Expression>(expr, builder_->create<sem::Void>(),
-                                                         current_statement_, sem::Constant{},
+                                                         current_statement_,
+                                                         /* constant_value */ nullptr,
                                                          /* has_side_effects */ false);
             },
             [&](Default) {
@@ -1168,23 +1298,24 @@
     // Helper for actually creating the the materialize node, performing the constant cast, updating
     // the ast -> sem binding, and performing validation.
     auto materialize = [&](const sem::Type* target_ty) -> sem::Materialize* {
+        auto* src_ty = expr->Type();
         auto* decl = expr->Declaration();
+        if (!validator_.Materialize(target_ty, src_ty, decl->source)) {
+            return nullptr;
+        }
         auto expr_val = EvaluateConstantValue(decl, expr->Type());
         if (!expr_val) {
-            return nullptr;
-        }
-        if (!expr_val->IsValid()) {
             TINT_ICE(Resolver, builder_->Diagnostics())
-                << decl->source
-                << "EvaluateConstantValue() returned invalid value for materialized value of type: "
-                << builder_->FriendlyName(expr->Type());
+                << decl->source << "EvaluateConstantValue(" << decl->TypeInfo().name
+                << ") returned invalid value";
             return nullptr;
         }
-        auto materialized_val = ConvertValue(expr_val.Get(), target_ty, decl->source);
+        auto materialized_val = ConvertValue(expr_val, target_ty, decl->source);
         if (!materialized_val) {
+            // ConvertValue() has already failed and raised an diagnostic error.
             return nullptr;
         }
-        if (!materialized_val->IsValid()) {
+        if (!materialized_val.Get()) {
             TINT_ICE(Resolver, builder_->Diagnostics())
                 << decl->source << "ConvertValue(" << builder_->FriendlyName(expr_val->Type())
                 << " -> " << builder_->FriendlyName(target_ty) << ") returned invalid value";
@@ -1194,7 +1325,7 @@
             builder_->create<sem::Materialize>(expr, current_statement_, materialized_val.Get());
         m->Behaviors() = expr->Behaviors();
         builder_->Sem().Replace(decl, m);
-        return validator_.Materialize(m) ? m : nullptr;
+        return m;
     };
 
     // Helpers for constructing semantic types
@@ -1289,12 +1420,10 @@
     }
 
     auto val = EvaluateConstantValue(expr, ty);
-    if (!val) {
-        return nullptr;
-    }
     bool has_side_effects = idx->HasSideEffects() || obj->HasSideEffects();
-    auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_, val.Get(),
-                                                  has_side_effects, obj->SourceVariable());
+    auto* sem = builder_->create<sem::IndexAccessorExpression>(
+        expr, ty, obj, idx, current_statement_, std::move(val), has_side_effects,
+        obj->SourceVariable());
     sem->Behaviors() = idx->Behaviors() + obj->Behaviors();
     return sem;
 }
@@ -1310,10 +1439,7 @@
     }
 
     auto val = EvaluateConstantValue(expr, ty);
-    if (!val) {
-        return nullptr;
-    }
-    auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_, val.Get(),
+    auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_, std::move(val),
                                                   inner->HasSideEffects());
 
     sem->Behaviors() = inner->Behaviors();
@@ -1361,11 +1487,8 @@
             return nullptr;
         }
         auto val = EvaluateConstantValue(expr, call_target->ReturnType());
-        if (!val) {
-            return nullptr;
-        }
         return builder_->create<sem::Call>(expr, call_target, std::move(args), current_statement_,
-                                           val.Get(), has_side_effects);
+                                           std::move(val), has_side_effects);
     };
 
     // ct_ctor_or_conv is a helper for building either a sem::TypeConstructor or sem::TypeConversion
@@ -1381,6 +1504,7 @@
             },
             [&](const sem::I32*) { return ct_ctor_or_conv(CtorConvIntrinsic::kI32, nullptr); },
             [&](const sem::U32*) { return ct_ctor_or_conv(CtorConvIntrinsic::kU32, nullptr); },
+            [&](const sem::F16*) { return ct_ctor_or_conv(CtorConvIntrinsic::kF16, nullptr); },
             [&](const sem::F32*) { return ct_ctor_or_conv(CtorConvIntrinsic::kF32, nullptr); },
             [&](const sem::Bool*) { return ct_ctor_or_conv(CtorConvIntrinsic::kBool, nullptr); },
             [&](const sem::Array* arr) -> sem::Call* {
@@ -1402,11 +1526,9 @@
                     return nullptr;
                 }
                 auto val = EvaluateConstantValue(expr, call_target->ReturnType());
-                if (!val) {
-                    return nullptr;
-                }
                 return builder_->create<sem::Call>(expr, call_target, std::move(args),
-                                                   current_statement_, val.Get(), has_side_effects);
+                                                   current_statement_, std::move(val),
+                                                   has_side_effects);
             },
             [&](const sem::Struct* str) -> sem::Call* {
                 auto* call_target = utils::GetOrCreate(
@@ -1427,11 +1549,9 @@
                     return nullptr;
                 }
                 auto val = EvaluateConstantValue(expr, call_target->ReturnType());
-                if (!val) {
-                    return nullptr;
-                }
                 return builder_->create<sem::Call>(expr, call_target, std::move(args),
-                                                   current_statement_, val.Get(), has_side_effects);
+                                                   current_statement_, std::move(val),
+                                                   has_side_effects);
             },
             [&](Default) {
                 AddError("type is not constructible", expr->source);
@@ -1560,9 +1680,9 @@
     }
 
     // If the builtin is @const, and all arguments have constant values, evaluate the builtin now.
-    sem::Constant constant;
+    const sem::Constant* constant = nullptr;
     if (builtin.const_eval_fn) {
-        std::vector<sem::Constant> values(args.size());
+        std::vector<const sem::Constant*> values(args.size());
         bool is_const = true;  // all arguments have constant values
         for (size_t i = 0; i < values.size(); i++) {
             if (auto v = args[i]->ConstantValue()) {
@@ -1613,12 +1733,13 @@
     if (texture_index == -1) {
         TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
     }
-    auto* texture = args[texture_index]->As<sem::VariableUser>()->Variable();
+    auto* texture = args[static_cast<size_t>(texture_index)]->As<sem::VariableUser>()->Variable();
     if (!texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
         int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
         const sem::Variable* sampler =
-            sampler_index != -1 ? args[sampler_index]->As<sem::VariableUser>()->Variable()
-                                : nullptr;
+            sampler_index != -1
+                ? args[static_cast<size_t>(sampler_index)]->As<sem::VariableUser>()->Variable()
+                : nullptr;
         current_function_->AddTextureSamplerPair(texture, sampler);
     }
 }
@@ -1638,7 +1759,7 @@
     // effects.
     bool has_side_effects = true;
     auto* call = builder_->create<sem::Call>(expr, target, std::move(args), current_statement_,
-                                             sem::Constant{}, has_side_effects);
+                                             /* constant_value */ nullptr, has_side_effects);
 
     target->AddCallSite(call);
 
@@ -1733,18 +1854,15 @@
     }
 
     auto val = EvaluateConstantValue(literal, ty);
-    if (!val) {
-        return nullptr;
-    }
-    return builder_->create<sem::Expression>(literal, ty, current_statement_, val.Get(),
+    return builder_->create<sem::Expression>(literal, ty, current_statement_, std::move(val),
                                              /* has_side_effects */ false);
 }
 
 sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
     auto symbol = expr->symbol;
     auto* resolved = sem_.ResolvedSymbol(expr);
-    if (auto* var = As<sem::Variable>(resolved)) {
-        auto* user = builder_->create<sem::VariableUser>(expr, current_statement_, var);
+    if (auto* variable = As<sem::Variable>(resolved)) {
+        auto* user = builder_->create<sem::VariableUser>(expr, current_statement_, variable);
 
         if (current_statement_) {
             // If identifier is part of a loop continuing block, make sure it
@@ -1780,12 +1898,20 @@
         }
 
         if (current_function_) {
-            if (auto* global = var->As<sem::GlobalVariable>()) {
+            if (auto* global = variable->As<sem::GlobalVariable>()) {
                 current_function_->AddDirectlyReferencedGlobal(global);
             }
+        } else if (variable->Declaration()->Is<ast::Var>()) {
+            // Use of a module-scope 'var' outside of a function.
+            // Note: The spec is currently vague around the rules here. See
+            // https://github.com/gpuweb/gpuweb/issues/3081. Remove this comment when resolved.
+            std::string desc = "var '" + builder_->Symbols().NameFor(symbol) + "' ";
+            AddError(desc + "cannot not be referenced at module-scope", expr->source);
+            AddNote(desc + "declared here", variable->Declaration()->source);
+            return nullptr;
         }
 
-        var->AddUser(user);
+        variable->AddUser(user);
         return user;
     }
 
@@ -1819,9 +1945,9 @@
     const sem::Type* ret = nullptr;
     std::vector<uint32_t> swizzle;
 
-    // Structure may be a side-effecting expression (e.g. function call).
-    auto* sem_structure = sem_.Get(expr->structure);
-    bool has_side_effects = sem_structure && sem_structure->HasSideEffects();
+    // Object may be a side-effecting expression (e.g. function call).
+    auto* object = sem_.Get(expr->structure);
+    bool has_side_effects = object && object->HasSideEffects();
 
     if (auto* str = storage_ty->As<sem::Struct>()) {
         Mark(expr->member);
@@ -1847,8 +1973,8 @@
             ret = builder_->create<sem::Reference>(ret, ref->StorageClass(), ref->Access());
         }
 
-        return builder_->create<sem::StructMemberAccess>(expr, ret, current_statement_, member,
-                                                         has_side_effects, source_var);
+        return builder_->create<sem::StructMemberAccess>(expr, ret, current_statement_, object,
+                                                         member, has_side_effects, source_var);
     }
 
     if (auto* vec = storage_ty->As<sem::Vector>()) {
@@ -1914,8 +2040,8 @@
             // the swizzle.
             ret = builder_->create<sem::Vector>(vec->type(), static_cast<uint32_t>(size));
         }
-        return builder_->create<sem::Swizzle>(expr, ret, current_statement_, std::move(swizzle),
-                                              has_side_effects, source_var);
+        return builder_->create<sem::Swizzle>(expr, ret, current_statement_, object,
+                                              std::move(swizzle), has_side_effects, source_var);
     }
 
     AddError("invalid member accessor expression. Expected vector or struct, got '" +
@@ -1948,12 +2074,9 @@
     }
 
     auto val = EvaluateConstantValue(expr, op.result);
-    if (!val) {
-        return nullptr;
-    }
     bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
-    auto* sem = builder_->create<sem::Expression>(expr, op.result, current_statement_, val.Get(),
-                                                  has_side_effects);
+    auto* sem = builder_->create<sem::Expression>(expr, op.result, current_statement_,
+                                                  std::move(val), has_side_effects);
     sem->Behaviors() = lhs->Behaviors() + rhs->Behaviors();
 
     return sem;
@@ -2025,10 +2148,7 @@
     }
 
     auto val = EvaluateConstantValue(unary, ty);
-    if (!val) {
-        return nullptr;
-    }
-    auto* sem = builder_->create<sem::Expression>(unary, ty, current_statement_, val.Get(),
+    auto* sem = builder_->create<sem::Expression>(unary, ty, current_statement_, std::move(val),
                                                   expr->HasSideEffects(), source_var);
     sem->Behaviors() = expr->Behaviors();
     return sem;
@@ -2099,69 +2219,47 @@
 
     uint64_t stride = explicit_stride ? explicit_stride : implicit_stride;
 
+    int64_t count = 0;  // sem::Array uses a size of 0 for a runtime-sized array.
+
     // Evaluate the constant array size expression.
-    // sem::Array uses a size of 0 for a runtime-sized array.
-    uint32_t count = 0;
     if (auto* count_expr = arr->count) {
         const auto* count_sem = Materialize(Expression(count_expr));
         if (!count_sem) {
             return nullptr;
         }
 
-        auto size_source = count_expr->source;
-
-        auto* ty = count_sem->Type()->UnwrapRef();
-        if (!ty->is_integer_scalar()) {
-            AddError("array size must be integer scalar", size_source);
-            return nullptr;
-        }
-
-        constexpr const char* kErrInvalidExpr =
-            "array size identifier must be a literal or a module-scope 'let'";
-
-        if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
-            // Make sure the identifier is a non-overridable module-scope 'let'.
-            auto* global = sem_.ResolvedSymbol<sem::GlobalVariable>(ident);
-            if (!global || !global->Declaration()->Is<ast::Let>()) {
-                AddError(kErrInvalidExpr, size_source);
-                return nullptr;
-            }
-            count_expr = global->Declaration()->constructor;
-        } else if (!count_expr->Is<ast::LiteralExpression>()) {
-            AddError(kErrInvalidExpr, size_source);
-            return nullptr;
-        }
-
-        auto count_val = count_sem->ConstantValue();
+        auto* count_val = count_sem->ConstantValue();
         if (!count_val) {
-            TINT_ICE(Resolver, diagnostics_) << "could not resolve array size expression";
+            AddError("array size must evaluate to a constant integer expression",
+                     count_expr->source);
             return nullptr;
         }
 
-        if (count_val.Element<AInt>(0).value < 1) {
-            AddError("array size must be at least 1", size_source);
+        if (auto* ty = count_val->Type(); !ty->is_integer_scalar()) {
+            AddError("array size must evaluate to a constant integer expression, but is type '" +
+                         builder_->FriendlyName(ty) + "'",
+                     count_expr->source);
             return nullptr;
         }
 
-        count = count_val.Element<uint32_t>(0);
+        count = count_val->As<AInt>();
+        if (count < 1) {
+            AddError("array size (" + std::to_string(count) + ") must be greater than 0",
+                     count_expr->source);
+            return nullptr;
+        }
     }
 
-    auto size = std::max<uint64_t>(count, 1) * stride;
+    auto size = std::max<uint64_t>(static_cast<uint32_t>(count), 1u) * stride;
     if (size > std::numeric_limits<uint32_t>::max()) {
         std::stringstream msg;
-        msg << "array size in bytes must not exceed 0x" << std::hex
-            << std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex << size;
+        msg << "array size (0x" << std::hex << size << ") must not exceed 0xffffffff bytes";
         AddError(msg.str(), arr->source);
         return nullptr;
     }
-    if (stride > std::numeric_limits<uint32_t>::max() ||
-        implicit_stride > std::numeric_limits<uint32_t>::max()) {
-        TINT_ICE(Resolver, diagnostics_) << "calculated array stride exceeds uint32";
-        return nullptr;
-    }
     auto* out = builder_->create<sem::Array>(
-        elem_type, count, el_align, static_cast<uint32_t>(size), static_cast<uint32_t>(stride),
-        static_cast<uint32_t>(implicit_stride));
+        elem_type, static_cast<uint32_t>(count), el_align, static_cast<uint32_t>(size),
+        static_cast<uint32_t>(stride), static_cast<uint32_t>(implicit_stride));
 
     if (!validator_.Array(out, source)) {
         return nullptr;
@@ -2289,8 +2387,8 @@
         offset = utils::RoundUp(align, offset);
         if (offset > std::numeric_limits<uint32_t>::max()) {
             std::stringstream msg;
-            msg << "struct member has byte offset 0x" << std::hex << offset
-                << ", but must not exceed 0x" << std::hex << std::numeric_limits<uint32_t>::max();
+            msg << "struct member offset (0x" << std::hex << offset << ") must not exceed 0x"
+                << std::hex << std::numeric_limits<uint32_t>::max() << " bytes";
             AddError(msg.str(), member->source);
             return nullptr;
         }
@@ -2311,8 +2409,7 @@
 
     if (struct_size > std::numeric_limits<uint32_t>::max()) {
         std::stringstream msg;
-        msg << "struct size in bytes must not exceed 0x" << std::hex
-            << std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex << struct_size;
+        msg << "struct size (0x" << std::hex << struct_size << ") must not exceed 0xffffffff bytes";
         AddError(msg.str(), str->source);
         return nullptr;
     }
@@ -2337,6 +2434,8 @@
                 break;
             }
         }
+
+        const_cast<sem::StructMember*>(sem_members[i])->SetStruct(out);
     }
 
     auto stage = current_function_ ? current_function_->Declaration()->PipelineStage()
@@ -2449,8 +2548,8 @@
     return StatementScope(stmt, sem, [&] {
         Mark(stmt->variable);
 
-        auto* var = Variable(stmt->variable, /* is_global */ false);
-        if (!var) {
+        auto* variable = Variable(stmt->variable, /* is_global */ false);
+        if (!variable) {
             return false;
         }
 
@@ -2466,11 +2565,11 @@
             current_block_->AddDecl(stmt->variable);
         }
 
-        if (auto* ctor = var->Constructor()) {
+        if (auto* ctor = variable->Constructor()) {
             sem->Behaviors() = ctor->Behaviors();
         }
 
-        return validator_.Variable(var);
+        return validator_.Variable(variable);
     });
 }
 
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index e75c952..1a45bc4 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -171,17 +171,25 @@
                              const ast::ExpressionList& params,
                              uint32_t* id);
 
-    //////////////////////////////////////////////////////////////////////////////
-    // AST and Type traversal methods
-    //////////////////////////////////////////////////////////////////////////////
+    /// Expression traverses the graph of expressions starting at `expr`, building a postordered
+    /// list (leaf-first) of all the expression nodes. Each of the expressions are then resolved by
+    /// dispatching to the appropriate expression handlers below.
+    /// @returns the resolved semantic node for the expression `expr`, or nullptr on failure.
+    sem::Expression* Expression(const ast::Expression* expr);
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////
     // Expression resolving methods
+    //
     // Returns the semantic node pointer on success, nullptr on failure.
+    //
+    // These methods are invoked by Expression(), in postorder (child-first). These methods should
+    // not attempt to resolve their children. This design avoids recursion, which is a common cause
+    // of stack-overflows.
+    ////////////////////////////////////////////////////////////////////////////////////////////////
     sem::Expression* IndexAccessor(const ast::IndexAccessorExpression*);
     sem::Expression* Binary(const ast::BinaryExpression*);
     sem::Expression* Bitcast(const ast::BitcastExpression*);
     sem::Call* Call(const ast::CallExpression*);
-    sem::Expression* Expression(const ast::Expression*);
     sem::Function* Function(const ast::Function*);
     sem::Call* FunctionCall(const ast::CallExpression*,
                             sem::Function* target,
@@ -195,6 +203,39 @@
     sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
     sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    /// Constant value evaluation methods
+    ///
+    /// These methods are called from the expression resolving methods, and so child-expression
+    /// nodes are guaranteed to have been already resolved and any constant values calculated.
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    const sem::Constant* EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type);
+    const sem::Constant* EvaluateConstantValue(const ast::IdentifierExpression* ident,
+                                               const sem::Type* type);
+    const sem::Constant* EvaluateConstantValue(const ast::LiteralExpression* literal,
+                                               const sem::Type* type);
+    const sem::Constant* EvaluateConstantValue(const ast::CallExpression* call,
+                                               const sem::Type* type);
+    const sem::Constant* EvaluateConstantValue(const ast::IndexAccessorExpression* call,
+                                               const sem::Type* type);
+
+    /// The result type of a ConstantEvaluation method.
+    /// Can be one of three distinct values:
+    /// * A non-null sem::Constant pointer. Returned when a expression resolves to a creation time
+    ///   value.
+    /// * A null sem::Constant pointer. Returned when a expression cannot resolve to a creation time
+    ///   value, but is otherwise legal.
+    /// * `utils::Failure`. Returned when there was a resolver error. In this situation the method
+    ///   will have already reported a diagnostic error message, and the caller should abort
+    ///   resolving.
+    using ConstantResult = utils::Result<const sem::Constant*>;
+
+    /// Convert the `value` to `target_type`
+    /// @return the converted value
+    ConstantResult ConvertValue(const sem::Constant* value,
+                                const sem::Type* target_type,
+                                const Source& source);
+
     /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
     /// If `expr` is of an abstract-numeric type:
     /// * Materialize will create and return a sem::Materialize node wrapping `expr`.
@@ -298,6 +339,37 @@
     /// @param is_global true if this is module scope, otherwise function scope
     sem::Variable* Variable(const ast::Variable* var, bool is_global);
 
+    /// @returns the semantic info for the `ast::Let` `v`. If an error is raised, nullptr is
+    /// returned.
+    /// @note this method does not resolve the attributes as these are context-dependent (global,
+    /// local)
+    /// @param var the variable
+    /// @param is_global true if this is module scope, otherwise function scope
+    sem::Variable* Let(const ast::Let* var, bool is_global);
+
+    /// @returns the semantic info for the module-scope `ast::Override` `v`. If an error is raised,
+    /// nullptr is returned.
+    /// @note this method does not resolve the attributes as these are context-dependent (global,
+    /// local)
+    /// @param override the variable
+    sem::Variable* Override(const ast::Override* override);
+
+    /// @returns the semantic info for an `ast::Const` `v`. If an error is raised, nullptr is
+    /// returned.
+    /// @note this method does not resolve the attributes as these are context-dependent (global,
+    /// local)
+    /// @param const_ the variable
+    /// @param is_global true if this is module scope, otherwise function scope
+    sem::Variable* Const(const ast::Const* const_, bool is_global);
+
+    /// @returns the semantic info for the `ast::Var` `var`. If an error is raised, nullptr is
+    /// returned.
+    /// @note this method does not resolve the attributes as these are context-dependent (global,
+    /// local)
+    /// @param var the variable
+    /// @param is_global true if this is module scope, otherwise function scope
+    sem::Variable* Var(const ast::Var* var, bool is_global);
+
     /// @returns the semantic info for the function parameter `param`. If an error is raised,
     /// nullptr is returned.
     /// @note the caller is expected to validate the parameter
@@ -356,23 +428,6 @@
     /// Adds the given note message to the diagnostics
     void AddNote(const std::string& msg, const Source& source) const;
 
-    //////////////////////////////////////////////////////////////////////////////
-    /// Constant value evaluation methods
-    //////////////////////////////////////////////////////////////////////////////
-    /// The result type of a ConstantEvaluation method. Holds the constant value and a boolean,
-    /// which is true on success, false on an error.
-    using ConstantResult = utils::Result<sem::Constant>;
-
-    /// Convert the `value` to `target_type`
-    /// @return the converted value
-    ConstantResult ConvertValue(const sem::Constant& value,
-                                const sem::Type* target_type,
-                                const Source& source);
-    ConstantResult EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type);
-    ConstantResult EvaluateConstantValue(const ast::LiteralExpression* literal,
-                                         const sem::Type* type);
-    ConstantResult EvaluateConstantValue(const ast::CallExpression* call, const sem::Type* type);
-
     /// @returns true if the symbol is the name of a builtin function.
     bool IsBuiltin(Symbol) const;
 
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index 24c57d4..f6ffb28 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -14,7 +14,6 @@
 
 #include "src/tint/resolver/resolver.h"
 
-#include <cmath>
 #include <optional>
 
 #include "src/tint/sem/abstract_float.h"
@@ -22,8 +21,6 @@
 #include "src/tint/sem/constant.h"
 #include "src/tint/sem/type_constructor.h"
 #include "src/tint/utils/compiler_macros.h"
-#include "src/tint/utils/map.h"
-#include "src/tint/utils/transform.h"
 
 using namespace tint::number_suffixes;  // NOLINT
 
@@ -31,244 +28,559 @@
 
 namespace {
 
-/// Converts and returns all the element values of `in` to the type `T`, using the converter
-/// function `CONVERTER`.
-/// @param elements_in the vector of elements to be converted
-/// @param converter a function-like with the signature `void(TO&, FROM)`
-/// @returns the elements converted to type T.
-template <typename T, typename ELEMENTS_IN, typename CONVERTER>
-sem::Constant::Elements Transform(const ELEMENTS_IN& elements_in, CONVERTER&& converter) {
-    TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
+/// TypeDispatch is a helper for calling the function `f`, passing a single zero-value argument of
+/// the C++ type that corresponds to the sem::Type `type`. For example, calling `TypeDispatch()`
+/// with a type of `sem::I32*` will call the function f with a single argument of `i32(0)`.
+/// @returns the value returned by calling `f`.
+/// @note `type` must be a scalar or abstract numeric type. Other types will not call `f`, and will
+/// return the zero-initialized value of the return type for `f`.
+template <typename F>
+auto TypeDispatch(const sem::Type* type, F&& f) {
+    return Switch(
+        type,                                                     //
+        [&](const sem::AbstractInt*) { return f(AInt(0)); },      //
+        [&](const sem::AbstractFloat*) { return f(AFloat(0)); },  //
+        [&](const sem::I32*) { return f(i32(0)); },               //
+        [&](const sem::U32*) { return f(u32(0)); },               //
+        [&](const sem::F32*) { return f(f32(0)); },               //
+        [&](const sem::F16*) { return f(f16(0)); },               //
+        [&](const sem::Bool*) { return f(static_cast<bool>(0)); });
+}
 
-    return utils::Transform(elements_in, [&](auto value_in) {
-        if constexpr (std::is_same_v<UnwrapNumber<T>, bool>) {
-            return AInt(value_in != 0);
+/// @returns `value` if `T` is not a Number, otherwise ValueOf returns the inner value of the
+/// Number.
+template <typename T>
+inline auto ValueOf(T value) {
+    if constexpr (std::is_same_v<UnwrapNumber<T>, T>) {
+        return value;
+    } else {
+        return value.value;
+    }
+}
+
+/// @returns true if `value` is a positive zero.
+template <typename T>
+inline bool IsPositiveZero(T value) {
+    using N = UnwrapNumber<T>;
+    return Number<N>(value) == Number<N>(0);  // Considers sign bit
+}
+
+/// Constant inherits from sem::Constant to add an private implementation method for conversion.
+struct Constant : public sem::Constant {
+    /// Convert attempts to convert the constant value to the given type. On error, Convert()
+    /// creates a new diagnostic message and returns a Failure.
+    virtual utils::Result<const Constant*> Convert(ProgramBuilder& builder,
+                                                   const sem::Type* target_ty,
+                                                   const Source& source) const = 0;
+};
+
+// Forward declaration
+const Constant* CreateComposite(ProgramBuilder& builder,
+                                const sem::Type* type,
+                                std::vector<const Constant*> elements);
+
+/// Element holds a single scalar or abstract-numeric value.
+/// Element implements the Constant interface.
+template <typename T>
+struct Element : Constant {
+    Element(const sem::Type* t, T v) : type(t), value(v) {}
+    ~Element() override = default;
+    const sem::Type* Type() const override { return type; }
+    std::variant<std::monostate, AInt, AFloat> Value() const override {
+        if constexpr (IsFloatingPoint<UnwrapNumber<T>>) {
+            return static_cast<AFloat>(value);
         } else {
-            T converted{};
-            converter(converted, value_in);
-            if constexpr (IsFloatingPoint<UnwrapNumber<T>>) {
-                return AFloat(converted);
+            return static_cast<AInt>(value);
+        }
+    }
+    const Constant* Index(size_t) const override { return nullptr; }
+    bool AllZero() const override { return IsPositiveZero(value); }
+    bool AnyZero() const override { return IsPositiveZero(value); }
+    bool AllEqual() const override { return true; }
+    size_t Hash() const override { return utils::Hash(type, ValueOf(value)); }
+
+    utils::Result<const Constant*> Convert(ProgramBuilder& builder,
+                                           const sem::Type* target_ty,
+                                           const Source& source) const override {
+        TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
+        if (target_ty == type) {
+            // If the types are identical, then no conversion is needed.
+            return this;
+        }
+        bool failed = false;
+        auto* res = TypeDispatch(target_ty, [&](auto zero_to) -> const Constant* {
+            // `T` is the source type, `value` is the source value.
+            // `TO` is the target type.
+            using TO = std::decay_t<decltype(zero_to)>;
+            if constexpr (std::is_same_v<TO, bool>) {
+                // [x -> bool]
+                return builder.create<Element<TO>>(target_ty, !IsPositiveZero(value));
+            } else if constexpr (std::is_same_v<T, bool>) {
+                // [bool -> x]
+                return builder.create<Element<TO>>(target_ty, TO(value ? 1 : 0));
+            } else if (auto conv = CheckedConvert<TO>(value)) {
+                // Conversion success
+                return builder.create<Element<TO>>(target_ty, conv.Get());
+                // --- Below this point are the failure cases ---
+            } else if constexpr (std::is_same_v<T, AInt> || std::is_same_v<T, AFloat>) {
+                // [abstract-numeric -> x] - materialization failure
+                std::stringstream ss;
+                ss << "value " << value << " cannot be represented as ";
+                ss << "'" << builder.FriendlyName(target_ty) << "'";
+                builder.Diagnostics().add_error(tint::diag::System::Resolver, ss.str(), source);
+                failed = true;
+            } else if constexpr (IsFloatingPoint<UnwrapNumber<TO>>) {
+                // [x -> floating-point] - number not exactly representable
+                // https://www.w3.org/TR/WGSL/#floating-point-conversion
+                constexpr auto kInf = std::numeric_limits<double>::infinity();
+                switch (conv.Failure()) {
+                    case ConversionFailure::kExceedsNegativeLimit:
+                        return builder.create<Element<TO>>(target_ty, TO(-kInf));
+                    case ConversionFailure::kExceedsPositiveLimit:
+                        return builder.create<Element<TO>>(target_ty, TO(kInf));
+                }
             } else {
-                return AInt(converted);
+                // [x -> integer] - number not exactly representable
+                // https://www.w3.org/TR/WGSL/#floating-point-conversion
+                switch (conv.Failure()) {
+                    case ConversionFailure::kExceedsNegativeLimit:
+                        return builder.create<Element<TO>>(target_ty, TO(TO::kLowest));
+                    case ConversionFailure::kExceedsPositiveLimit:
+                        return builder.create<Element<TO>>(target_ty, TO(TO::kHighest));
+                }
             }
+            return nullptr;  // Expression is not constant.
+        });
+        if (failed) {
+            // A diagnostic error has been raised, and resolving should abort.
+            return utils::Failure;
         }
-    });
-
-    TINT_END_DISABLE_WARNING(UNREACHABLE_CODE);
-}
-
-/// Converts and returns all the element values of `in` to the semantic type `el_ty`, using the
-/// converter function `CONVERTER`.
-/// @param in the constant to convert
-/// @param el_ty the target element type
-/// @param converter a function-like with the signature `void(TO&, FROM)`
-/// @returns the elements converted to `el_ty`
-template <typename CONVERTER>
-sem::Constant::Elements Transform(const sem::Constant::Elements& in,
-                                  const sem::Type* el_ty,
-                                  CONVERTER&& converter) {
-    return std::visit(
-        [&](auto&& v) {
-            return Switch(
-                el_ty,  //
-                [&](const sem::AbstractInt*) { return Transform<AInt>(v, converter); },
-                [&](const sem::AbstractFloat*) { return Transform<AFloat>(v, converter); },
-                [&](const sem::I32*) { return Transform<i32>(v, converter); },
-                [&](const sem::U32*) { return Transform<u32>(v, converter); },
-                [&](const sem::F32*) { return Transform<f32>(v, converter); },
-                [&](const sem::F16*) { return Transform<f16>(v, converter); },
-                [&](const sem::Bool*) { return Transform<bool>(v, converter); },
-                [&](Default) -> sem::Constant::Elements {
-                    diag::List diags;
-                    TINT_UNREACHABLE(Semantic, diags)
-                        << "invalid element type " << el_ty->TypeInfo().name;
-                    return {};
-                });
-        },
-        in);
-}
-
-/// Converts and returns all the elements in `in` to the type `el_ty`.
-/// If the value does not fit in the target type, and:
-///  * the target type is an integer type, then the resulting value will be clamped to the integer's
-///    highest or lowest value.
-///  * the target type is an float type, then the resulting value will be either positive or
-///    negative infinity, based on the sign of the input value.
-/// @param in the input elements
-/// @param el_ty the target element type
-/// @returns the elements converted to `el_ty`
-sem::Constant::Elements ConvertElements(const sem::Constant::Elements& in, const sem::Type* el_ty) {
-    return Transform(in, el_ty, [](auto& el_out, auto el_in) {
-        using OUT = std::decay_t<decltype(el_out)>;
-        if (auto conv = CheckedConvert<OUT>(el_in)) {
-            el_out = conv.Get();
-        } else {
-            constexpr auto kInf = std::numeric_limits<double>::infinity();
-            switch (conv.Failure()) {
-                case ConversionFailure::kExceedsNegativeLimit:
-                    el_out = IsFloatingPoint<UnwrapNumber<OUT>> ? OUT(-kInf) : OUT::kLowest;
-                    break;
-                case ConversionFailure::kExceedsPositiveLimit:
-                    el_out = IsFloatingPoint<UnwrapNumber<OUT>> ? OUT(kInf) : OUT::kHighest;
-                    break;
-            }
-        }
-    });
-}
-
-/// Converts and returns all the elements in `in` to the type `el_ty`, by performing a
-/// `CheckedConvert` on each element value. A single error diagnostic will be raised if an element
-/// value cannot be represented by the target type.
-/// @param in the input elements
-/// @param el_ty the target element type
-/// @returns the elements converted to `el_ty`, or a Failure if some elements could not be
-/// represented by the target type.
-utils::Result<sem::Constant::Elements> MaterializeElements(const sem::Constant::Elements& in,
-                                                           const sem::Type* el_ty,
-                                                           ProgramBuilder& builder,
-                                                           Source source) {
-    std::optional<std::string> failure;
-
-    auto out = Transform(in, el_ty, [&](auto& el_out, auto el_in) {
-        using OUT = std::decay_t<decltype(el_out)>;
-        if (auto conv = CheckedConvert<OUT>(el_in)) {
-            el_out = conv.Get();
-        } else if (!failure.has_value()) {
-            std::stringstream ss;
-            ss << "value " << el_in << " cannot be represented as ";
-            ss << "'" << builder.FriendlyName(el_ty) << "'";
-            failure = ss.str();
-        }
-    });
-
-    if (failure.has_value()) {
-        builder.Diagnostics().add_error(diag::System::Resolver, std::move(failure.value()), source);
-        return utils::Failure;
+        return res;
+        TINT_END_DISABLE_WARNING(UNREACHABLE_CODE);
     }
 
-    return out;
+    sem::Type const* const type;
+    const T value;
+};
+
+/// Splat holds a single Constant value, duplicated as all children.
+/// Splat is used for zero-initializers, 'splat' constructors, or constructors where each element is
+/// identical. Splat may be of a vector, matrix or array type.
+/// Splat implements the Constant interface.
+struct Splat : Constant {
+    Splat(const sem::Type* t, const Constant* e, size_t n) : type(t), el(e), count(n) {}
+    ~Splat() override = default;
+    const sem::Type* Type() const override { return type; }
+    std::variant<std::monostate, AInt, AFloat> Value() const override { return {}; }
+    const Constant* Index(size_t i) const override { return i < count ? el : nullptr; }
+    bool AllZero() const override { return el->AllZero(); }
+    bool AnyZero() const override { return el->AnyZero(); }
+    bool AllEqual() const override { return true; }
+    size_t Hash() const override { return utils::Hash(type, el->Hash(), count); }
+
+    utils::Result<const Constant*> Convert(ProgramBuilder& builder,
+                                           const sem::Type* target_ty,
+                                           const Source& source) const override {
+        // Convert the single splatted element type.
+        auto conv_el = el->Convert(builder, sem::Type::ElementOf(target_ty), source);
+        if (!conv_el) {
+            return utils::Failure;
+        }
+        if (!conv_el.Get()) {
+            return nullptr;
+        }
+        return builder.create<Splat>(target_ty, conv_el.Get(), count);
+    }
+
+    sem::Type const* const type;
+    const Constant* el;
+    const size_t count;
+};
+
+/// Composite holds a number of mixed child Constant values.
+/// Composite may be of a vector, matrix or array type.
+/// If each element is the same type and value, then a Splat would be a more efficient constant
+/// implementation. Use CreateComposite() to create the appropriate Constant type.
+/// Composite implements the Constant interface.
+struct Composite : Constant {
+    Composite(const sem::Type* t, std::vector<const Constant*> els, bool all_0, bool any_0)
+        : type(t), elements(std::move(els)), all_zero(all_0), any_zero(any_0), hash(CalcHash()) {}
+    ~Composite() override = default;
+    const sem::Type* Type() const override { return type; }
+    std::variant<std::monostate, AInt, AFloat> Value() const override { return {}; }
+    const Constant* Index(size_t i) const override {
+        return i < elements.size() ? elements[i] : nullptr;
+    }
+    bool AllZero() const override { return all_zero; }
+    bool AnyZero() const override { return any_zero; }
+    bool AllEqual() const override { return false; /* otherwise this should be a Splat */ }
+    size_t Hash() const override { return hash; }
+
+    utils::Result<const Constant*> Convert(ProgramBuilder& builder,
+                                           const sem::Type* target_ty,
+                                           const Source& source) const override {
+        // Convert each of the composite element types.
+        auto* el_ty = sem::Type::ElementOf(target_ty);
+        std::vector<const Constant*> conv_els;
+        conv_els.reserve(elements.size());
+        for (auto* el : elements) {
+            auto conv_el = el->Convert(builder, el_ty, source);
+            if (!conv_el) {
+                return utils::Failure;
+            }
+            if (!conv_el.Get()) {
+                return nullptr;
+            }
+            conv_els.emplace_back(conv_el.Get());
+        }
+        return CreateComposite(builder, target_ty, std::move(conv_els));
+    }
+
+    size_t CalcHash() {
+        auto h = utils::Hash(type, all_zero, any_zero);
+        for (auto* el : elements) {
+            utils::HashCombine(&h, el->Hash());
+        }
+        return h;
+    }
+
+    sem::Type const* const type;
+    const std::vector<const Constant*> elements;
+    const bool all_zero;
+    const bool any_zero;
+    const size_t hash;
+};
+
+/// CreateElement constructs and returns an Element<T>.
+template <typename T>
+const Constant* CreateElement(ProgramBuilder& builder, const sem::Type* t, T v) {
+    return builder.create<Element<T>>(t, v);
+}
+
+/// ZeroValue returns a Constant for the zero-value of the type `type`.
+const Constant* ZeroValue(ProgramBuilder& builder, const sem::Type* type) {
+    return Switch(
+        type,  //
+        [&](const sem::Vector* v) -> const Constant* {
+            auto* zero_el = ZeroValue(builder, v->type());
+            return builder.create<Splat>(type, zero_el, v->Width());
+        },
+        [&](const sem::Matrix* m) -> const Constant* {
+            auto* zero_el = ZeroValue(builder, m->ColumnType());
+            return builder.create<Splat>(type, zero_el, m->columns());
+        },
+        [&](const sem::Array* a) -> const Constant* {
+            if (auto* zero_el = ZeroValue(builder, a->ElemType())) {
+                return builder.create<Splat>(type, zero_el, a->Count());
+            }
+            return nullptr;
+        },
+        [&](Default) -> const Constant* {
+            return TypeDispatch(type, [&](auto zero) -> const Constant* {
+                return CreateElement(builder, type, zero);
+            });
+        });
+}
+
+/// Equal returns true if the constants `a` and `b` are of the same type and value.
+bool Equal(const sem::Constant* a, const sem::Constant* b) {
+    if (a->Hash() != b->Hash()) {
+        return false;
+    }
+    if (a->Type() != b->Type()) {
+        return false;
+    }
+    return Switch(
+        a->Type(),  //
+        [&](const sem::Vector* vec) {
+            for (size_t i = 0; i < vec->Width(); i++) {
+                if (!Equal(a->Index(i), b->Index(i))) {
+                    return false;
+                }
+            }
+            return true;
+        },
+        [&](const sem::Matrix* mat) {
+            for (size_t i = 0; i < mat->columns(); i++) {
+                if (!Equal(a->Index(i), b->Index(i))) {
+                    return false;
+                }
+            }
+            return true;
+        },
+        [&](const sem::Array* arr) {
+            for (size_t i = 0; i < arr->Count(); i++) {
+                if (!Equal(a->Index(i), b->Index(i))) {
+                    return false;
+                }
+            }
+            return true;
+        },
+        [&](Default) { return a->Value() == b->Value(); });
+}
+
+/// CreateComposite is used to construct a constant of a vector, matrix or array type.
+/// CreateComposite examines the element values and will return either a Composite or a Splat,
+/// depending on the element types and values.
+const Constant* CreateComposite(ProgramBuilder& builder,
+                                const sem::Type* type,
+                                std::vector<const Constant*> elements) {
+    if (elements.size() == 0) {
+        return nullptr;
+    }
+    bool any_zero = false;
+    bool all_zero = true;
+    bool all_equal = true;
+    auto* first = elements.front();
+    for (auto* el : elements) {
+        if (!any_zero && el->AnyZero()) {
+            any_zero = true;
+        }
+        if (all_zero && !el->AllZero()) {
+            all_zero = false;
+        }
+        if (all_equal && el != first) {
+            if (!Equal(el, first)) {
+                all_equal = false;
+            }
+        }
+    }
+    if (all_equal) {
+        return builder.create<Splat>(type, elements[0], elements.size());
+    } else {
+        return builder.create<Composite>(type, std::move(elements), all_zero, any_zero);
+    }
 }
 
 }  // namespace
 
-utils::Result<sem::Constant> Resolver::EvaluateConstantValue(const ast::Expression* expr,
-                                                             const sem::Type* type) {
-    if (auto* e = expr->As<ast::LiteralExpression>()) {
-        return EvaluateConstantValue(e, type);
-    }
-    if (auto* e = expr->As<ast::CallExpression>()) {
-        return EvaluateConstantValue(e, type);
-    }
-    return sem::Constant{};
+const sem::Constant* Resolver::EvaluateConstantValue(const ast::Expression* expr,
+                                                     const sem::Type* type) {
+    return Switch(
+        expr,  //
+        [&](const ast::IdentifierExpression* e) { return EvaluateConstantValue(e, type); },
+        [&](const ast::LiteralExpression* e) { return EvaluateConstantValue(e, type); },
+        [&](const ast::CallExpression* e) { return EvaluateConstantValue(e, type); },
+        [&](const ast::IndexAccessorExpression* e) { return EvaluateConstantValue(e, type); });
 }
 
-utils::Result<sem::Constant> Resolver::EvaluateConstantValue(const ast::LiteralExpression* literal,
-                                                             const sem::Type* type) {
+const sem::Constant* Resolver::EvaluateConstantValue(const ast::IdentifierExpression* ident,
+                                                     const sem::Type*) {
+    if (auto* sem = builder_->Sem().Get(ident)) {
+        return sem->ConstantValue();
+    }
+    return {};
+}
+
+const sem::Constant* Resolver::EvaluateConstantValue(const ast::LiteralExpression* literal,
+                                                     const sem::Type* type) {
     return Switch(
         literal,
         [&](const ast::BoolLiteralExpression* lit) {
-            return sem::Constant{type, {AInt(lit->value ? 1 : 0)}};
+            return CreateElement(*builder_, type, lit->value);
         },
-        [&](const ast::IntLiteralExpression* lit) {
-            return sem::Constant{type, {AInt(lit->value)}};
+        [&](const ast::IntLiteralExpression* lit) -> const Constant* {
+            switch (lit->suffix) {
+                case ast::IntLiteralExpression::Suffix::kNone:
+                    return CreateElement(*builder_, type, AInt(lit->value));
+                case ast::IntLiteralExpression::Suffix::kI:
+                    return CreateElement(*builder_, type, i32(lit->value));
+                case ast::IntLiteralExpression::Suffix::kU:
+                    return CreateElement(*builder_, type, u32(lit->value));
+            }
+            return nullptr;
         },
-        [&](const ast::FloatLiteralExpression* lit) {
-            return sem::Constant{type, {AFloat(lit->value)}};
+        [&](const ast::FloatLiteralExpression* lit) -> const Constant* {
+            switch (lit->suffix) {
+                case ast::FloatLiteralExpression::Suffix::kNone:
+                    return CreateElement(*builder_, type, AFloat(lit->value));
+                case ast::FloatLiteralExpression::Suffix::kF:
+                    return CreateElement(*builder_, type, f32(lit->value));
+                case ast::FloatLiteralExpression::Suffix::kH:
+                    return CreateElement(*builder_, type, f16(lit->value));
+            }
+            return nullptr;
         });
 }
 
-utils::Result<sem::Constant> Resolver::EvaluateConstantValue(const ast::CallExpression* call,
-                                                             const sem::Type* ty) {
-    uint32_t result_size = 0;
-    auto* el_ty = sem::Type::ElementOf(ty, &result_size);
-    if (!el_ty) {
-        return sem::Constant{};
-    }
-
-    // ElementOf() will also return the element type of array, which we do not support.
-    if (ty->Is<sem::Array>()) {
-        return sem::Constant{};
-    }
+const sem::Constant* Resolver::EvaluateConstantValue(const ast::CallExpression* call,
+                                                     const sem::Type* ty) {
+    // Note: we are building constant values for array types. The working group as verbally agreed
+    // to support constant expression arrays, but this is not (yet) part of the spec.
+    // See: https://github.com/gpuweb/gpuweb/issues/3056
 
     // For zero value init, return 0s
     if (call->args.empty()) {
-        return Switch(
-            el_ty,
-            [&](const sem::AbstractInt*) {
-                return sem::Constant(ty, std::vector(result_size, AInt(0)));
-            },
-            [&](const sem::AbstractFloat*) {
-                return sem::Constant(ty, std::vector(result_size, AFloat(0)));
-            },
-            [&](const sem::I32*) { return sem::Constant(ty, std::vector(result_size, AInt(0))); },
-            [&](const sem::U32*) { return sem::Constant(ty, std::vector(result_size, AInt(0))); },
-            [&](const sem::F32*) { return sem::Constant(ty, std::vector(result_size, AFloat(0))); },
-            [&](const sem::F16*) { return sem::Constant(ty, std::vector(result_size, AFloat(0))); },
-            [&](const sem::Bool*) { return sem::Constant(ty, std::vector(result_size, AInt(0))); });
+        return ZeroValue(*builder_, ty);
     }
 
-    // Build value for type_ctor from each child value by converting to type_ctor's type.
-    std::optional<sem::Constant::Elements> elements;
-    for (auto* expr : call->args) {
-        auto* arg = builder_->Sem().Get(expr);
+    uint32_t el_count = 0;
+    auto* el_ty = sem::Type::ElementOf(ty, &el_count);
+    if (!el_ty) {
+        return nullptr;  // Target type does not support constant values
+    }
+
+    // value_of returns a `const Constant*` for the expression `expr`, or nullptr if the expression
+    // does not have a constant value.
+    auto value_of = [&](const ast::Expression* expr) {
+        return static_cast<const Constant*>(builder_->Sem().Get(expr)->ConstantValue());
+    };
+
+    if (call->args.size() == 1) {
+        // Type constructor or conversion that takes a single argument.
+        auto& src = call->args[0]->source;
+        auto* arg = value_of(call->args[0]);
         if (!arg) {
-            return sem::Constant{};
-        }
-        auto value = arg->ConstantValue();
-        if (!value) {
-            return sem::Constant{};
+            return nullptr;  // Single argument is not constant.
         }
 
-        // Convert the elements to the desired type.
-        auto converted = ConvertElements(value.GetElements(), el_ty);
-
-        if (elements.has_value()) {
-            // Append the converted vector to elements
-            std::visit(
-                [&](auto&& dst) {
-                    using VEC_TY = std::decay_t<decltype(dst)>;
-                    const auto& src = std::get<VEC_TY>(converted);
-                    dst.insert(dst.end(), src.begin(), src.end());
-                },
-                elements.value());
-        } else {
-            elements = std::move(converted);
+        if (ty->is_scalar()) {  // Scalar type conversion: i32(x), u32(x), bool(x), etc
+            return ConvertValue(arg, el_ty, src).Get();
         }
+
+        if (arg->Type() == el_ty) {
+            // Argument type matches function type. This is a splat.
+            auto splat = [&](size_t n) { return builder_->create<Splat>(ty, arg, n); };
+            return Switch(
+                ty,  //
+                [&](const sem::Vector* v) { return splat(v->Width()); },
+                [&](const sem::Matrix* m) { return splat(m->columns()); },
+                [&](const sem::Array* a) { return splat(a->Count()); });
+        }
+
+        // Argument type and function type mismatch. This is a type conversion.
+        if (auto conv = ConvertValue(arg, ty, src)) {
+            return conv.Get();
+        }
+
+        return nullptr;
     }
 
-    // Splat single-value initializers
-    std::visit(
-        [&](auto&& v) {
-            if (v.size() == 1) {
-                for (uint32_t i = 0; i < result_size - 1; ++i) {
-                    v.emplace_back(v[0]);
+    // Multiple arguments. Must be a type constructor.
+
+    std::vector<const Constant*> els;  // The constant elements for the composite constant.
+    els.reserve(el_count);
+
+    // Helper for pushing all the argument constants to `els`.
+    auto push_all_args = [&] {
+        for (auto* expr : call->args) {
+            auto* arg = value_of(expr);
+            if (!arg) {
+                return;
+            }
+            els.emplace_back(arg);
+        }
+    };
+
+    Switch(
+        ty,  // What's the target type being constructed?
+        [&](const sem::Vector*) {
+            // Vector can be constructed with a mix of scalars / abstract numerics and smaller
+            // vectors.
+            for (auto* expr : call->args) {
+                auto* arg = value_of(expr);
+                if (!arg) {
+                    return;
+                }
+                auto* arg_ty = arg->Type();
+                if (auto* arg_vec = arg_ty->As<sem::Vector>()) {
+                    // Extract out vector elements.
+                    for (uint32_t i = 0; i < arg_vec->Width(); i++) {
+                        auto* el = static_cast<const Constant*>(arg->Index(i));
+                        if (!el) {
+                            return;
+                        }
+                        els.emplace_back(el);
+                    }
+                } else {
+                    els.emplace_back(arg);
                 }
             }
         },
-        elements.value());
+        [&](const sem::Matrix* m) {
+            // Matrix can be constructed with a set of scalars / abstract numerics, or column
+            // vectors.
+            if (call->args.size() == m->columns() * m->rows()) {
+                // Matrix built from scalars / abstract numerics
+                for (uint32_t c = 0; c < m->columns(); c++) {
+                    std::vector<const Constant*> column;
+                    column.reserve(m->rows());
+                    for (uint32_t r = 0; r < m->rows(); r++) {
+                        auto* arg = value_of(call->args[r + c * m->rows()]);
+                        if (!arg) {
+                            return;
+                        }
+                        column.emplace_back(arg);
+                    }
+                    els.push_back(CreateComposite(*builder_, m->ColumnType(), std::move(column)));
+                }
+            } else if (call->args.size() == m->columns()) {
+                // Matrix built from column vectors
+                push_all_args();
+            }
+        },
+        [&](const sem::Array*) {
+            // Arrays must be constructed using a list of elements
+            push_all_args();
+        });
 
-    return sem::Constant(ty, std::move(elements.value()));
+    if (els.size() != el_count) {
+        // If the number of constant elements doesn't match the type, then something went wrong.
+        return nullptr;
+    }
+    // Construct and return either a Composite or Splat.
+    return CreateComposite(*builder_, ty, std::move(els));
 }
 
-utils::Result<sem::Constant> Resolver::ConvertValue(const sem::Constant& value,
-                                                    const sem::Type* ty,
-                                                    const Source& source) {
-    if (value.Type() == ty) {
+const sem::Constant* Resolver::EvaluateConstantValue(const ast::IndexAccessorExpression* accessor,
+                                                     const sem::Type*) {
+    auto* obj_sem = builder_->Sem().Get(accessor->object);
+    if (!obj_sem) {
+        return {};
+    }
+
+    auto obj_val = obj_sem->ConstantValue();
+    if (!obj_val) {
+        return {};
+    }
+
+    auto* idx_sem = builder_->Sem().Get(accessor->index);
+    if (!idx_sem) {
+        return {};
+    }
+
+    auto idx_val = idx_sem->ConstantValue();
+    if (!idx_val) {
+        return {};
+    }
+
+    uint32_t el_count = 0;
+    sem::Type::ElementOf(obj_val->Type(), &el_count);
+
+    AInt idx = idx_val->As<AInt>();
+    if (idx < 0 || idx >= el_count) {
+        auto clamped = std::min<AInt::type>(std::max<AInt::type>(idx, 0), el_count - 1);
+        AddWarning("index " + std::to_string(idx) + " out of bounds [0.." +
+                       std::to_string(el_count - 1) + "]. Clamping index to " +
+                       std::to_string(clamped),
+                   accessor->index->source);
+        idx = clamped;
+    }
+
+    return obj_val->Index(static_cast<size_t>(idx));
+}
+
+utils::Result<const sem::Constant*> Resolver::ConvertValue(const sem::Constant* value,
+                                                           const sem::Type* target_ty,
+                                                           const Source& source) {
+    if (value->Type() == target_ty) {
         return value;
     }
-
-    auto* el_ty = sem::Type::ElementOf(ty);
-    if (el_ty == nullptr) {
-        return sem::Constant{};
+    auto conv = static_cast<const Constant*>(value)->Convert(*builder_, target_ty, source);
+    if (!conv) {
+        return utils::Failure;
     }
-    if (value.ElementType() == el_ty) {
-        return sem::Constant(ty, value.GetElements());
-    }
-
-    if (auto res = MaterializeElements(value.GetElements(), el_ty, *builder_, source)) {
-        return sem::Constant(ty, std::move(res.Get()));
-    }
-    return utils::Failure;
+    return conv.Get();
 }
 
 }  // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc
index bbdbfea..ff8a189 100644
--- a/src/tint/resolver/resolver_constants_test.cc
+++ b/src/tint/resolver/resolver_constants_test.cc
@@ -14,9 +14,13 @@
 
 #include "src/tint/resolver/resolver.h"
 
+#include <cmath>
+
 #include "gtest/gtest.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/expression.h"
+#include "src/tint/sem/index_accessor_expression.h"
+#include "src/tint/sem/test_helper.h"
 
 using namespace tint::number_suffixes;  // NOLINT
 
@@ -25,6 +29,10 @@
 
 using ResolverConstantsTest = ResolverTest;
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Construction
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 TEST_F(ResolverConstantsTest, Scalar_i32) {
     auto* expr = Expr(99_i);
     WrapInFunction(expr);
@@ -32,12 +40,13 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
+    ASSERT_NE(sem, nullptr);
     EXPECT_TRUE(sem->Type()->Is<sem::I32>());
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 99);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 99);
 }
 
 TEST_F(ResolverConstantsTest, Scalar_u32) {
@@ -47,12 +56,13 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
+    ASSERT_NE(sem, nullptr);
     EXPECT_TRUE(sem->Type()->Is<sem::U32>());
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 99u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<AInt>(), 99u);
 }
 
 TEST_F(ResolverConstantsTest, Scalar_f32) {
@@ -62,12 +72,31 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
+    ASSERT_NE(sem, nullptr);
     EXPECT_TRUE(sem->Type()->Is<sem::F32>());
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 9.9f);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<AFloat>().value, 9.9f);
+}
+
+TEST_F(ResolverConstantsTest, Scalar_f16) {
+    Enable(ast::Extension::kF16);
+    auto* expr = Expr(9.9_h);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    EXPECT_TRUE(sem->Type()->Is<sem::F16>());
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375
+    EXPECT_EQ(sem->ConstantValue()->As<AFloat>(), 9.8984375f);
 }
 
 TEST_F(ResolverConstantsTest, Scalar_bool) {
@@ -77,12 +106,13 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
+    ASSERT_NE(sem, nullptr);
     EXPECT_TRUE(sem->Type()->Is<sem::Bool>());
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<bool>(), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_i32) {
@@ -92,16 +122,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 0);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 0);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 0);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 0);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 0);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 0);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_u32) {
@@ -111,16 +155,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 0u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 0u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 0u);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::U32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 0u);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 0u);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 0u);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) {
@@ -130,16 +188,64 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 0._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0._a);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f16) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>();
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
     EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 0.0);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, 0.0);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 0.0);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 0._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0._a);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) {
@@ -149,16 +255,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), false);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), false);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), false);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), false);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_i32) {
@@ -168,16 +288,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 99);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 99);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 99);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 99);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 99);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 99);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_u32) {
@@ -187,16 +321,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 99u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 99u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 99u);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::U32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 99u);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 99u);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 99u);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_f32) {
@@ -206,16 +354,65 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 9.9f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 9.9f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 9.9f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Splat_f16) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(9.9_h);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
     EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 9.9f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, 9.9f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 9.9f);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    // 9.9 is not exactly representable by f16, and should be quantized to 9.8984375
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 9.8984375f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 9.8984375f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 9.8984375f);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Splat_bool) {
@@ -225,16 +422,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), true);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), true);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_i32) {
@@ -244,16 +455,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 1);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 2);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 3);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_u32) {
@@ -263,16 +488,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 1);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 2);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 3);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::U32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f32) {
@@ -282,16 +521,64 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f16) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(1_h, 2_h, 3_h);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
     EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 1.f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, 2.f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 3.f);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_FullConstruct_bool) {
@@ -301,16 +588,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), false);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_i32) {
@@ -320,16 +621,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 1);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 2);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 3);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_u32) {
@@ -339,16 +654,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 1);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 2);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 3);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::U32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32) {
@@ -358,16 +687,332 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_all_10) {
+    auto* expr = vec3<f32>(10_f, vec2<f32>(10_f, 10_f));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
     EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 1.f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, 2.f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 3.f);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 10_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 10_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 10_f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_all_positive_0) {
+    auto* expr = vec3<f32>(0_f, vec2<f32>(0_f, 0_f));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_all_negative_0) {
+    auto* expr = vec3<f32>(vec2<f32>(-0_f, -0_f), -0_f);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), -0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), -0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), -0_f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32_mixed_sign_0) {
+    auto* expr = vec3<f32>(0_f, vec2<f32>(-0_f, 0_f));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), -0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(1_h, vec2<f16>(2_h, 3_h));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 1.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 2.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 3.f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_all_10) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(10_h, vec2<f16>(10_h, 10_h));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 10_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), 10_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 10_h);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_all_positive_0) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(0_h, vec2<f16>(0_h, 0_h));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 0_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), 0_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 0_h);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_all_negative_0) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(vec2<f16>(-0_h, -0_h), -0_h);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), -0_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), -0_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), -0_h);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f16_mixed_sign_0) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(0_h, vec2<f16>(-0_h, 0_h));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f16>(), 0_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f16>(), -0_h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f16>(), 0_h);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) {
@@ -377,16 +1022,96 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(0), true);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(1), false);
-    EXPECT_EQ(sem->ConstantValue().Element<bool>(2), true);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_all_true) {
+    auto* expr = vec3<bool>(true, vec2<bool>(true, true));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), true);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), true);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), true);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_MixConstruct_all_false) {
+    auto* expr = vec3<bool>(false, vec2<bool>(false, false));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::Bool>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<bool>(), false);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<bool>(), false);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Convert_f32_to_i32) {
@@ -396,16 +1121,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, 1);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, 2);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, 3);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f32) {
@@ -415,16 +1154,98 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Convert_f16_to_i32) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
     EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 10.f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, 20.f);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 30.f);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Convert_u32_to_f16) {
+    Enable(ast::Extension::kF16);
+    auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_i32) {
@@ -434,16 +1255,30 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, i32::kHighest);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, i32::kLowest);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, i32::kHighest);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::I32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::kHighest);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::kLowest);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::kHighest);
 }
 
 TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_u32) {
@@ -453,23 +1288,36 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(0).value, u32::kHighest);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(1).value, u32::kLowest);
-    EXPECT_EQ(sem->ConstantValue().Element<AInt>(2).value, u32::kHighest);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::U32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::kHighest);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::kLowest);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::kHighest);
 }
 
-// TODO(crbug.com/tint/1502): Enable when f16 overloads are implemented
-TEST_F(ResolverConstantsTest, DISABLED_Vec3_Convert_Large_f32_to_f16) {
+TEST_F(ResolverConstantsTest, Vec3_Convert_Large_f32_to_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* expr = vec3<f16>(vec3<f32>(0.00001_f, -0.00002_f, 0.00003_f));
+    auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
     WrapInFunction(expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -477,38 +1325,923 @@
     constexpr auto kInf = std::numeric_limits<double>::infinity();
 
     auto* sem = Sem().Get(expr);
-    EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F16>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, kInf);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, -kInf);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, kInf);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInf);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInf);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInf);
 }
 
-// TODO(crbug.com/tint/1502): Enable when f16 overloads are implemented
-TEST_F(ResolverConstantsTest, DISABLED_Vec3_Convert_Small_f32_to_f16) {
+TEST_F(ResolverConstantsTest, Vec3_Convert_Small_f32_to_f16) {
     Enable(ast::Extension::kF16);
 
-    auto* expr = vec3<f16>(vec3<f32>(1e-10_f, -1e20_f, 1e30_f));
+    auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F16>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
+    EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
+    EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
+    EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
+}
+
+TEST_F(ResolverConstantsTest, Mat2x3_ZeroInit_f32) {
+    auto* expr = mat2x3<f32>();
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* mat = sem->Type()->As<sem::Matrix>();
+    ASSERT_NE(mat, nullptr);
+    EXPECT_TRUE(mat->type()->Is<sem::F32>());
+    EXPECT_EQ(mat->columns(), 2u);
+    EXPECT_EQ(mat->rows(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0._f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0._f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0._f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0._f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0._f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0._f);
+}
+
+TEST_F(ResolverConstantsTest, Mat2x3_ZeroInit_f16) {
+    Enable(ast::Extension::kF16);
+
+    auto* expr = mat2x3<f16>();
     WrapInFunction(expr);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = Sem().Get(expr);
     EXPECT_NE(sem, nullptr);
-    ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
-    EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
-    EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
-    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F16>());
-    ASSERT_EQ(sem->ConstantValue().ElementCount(), 3u);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(0).value, 0.0);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(1).value, -0.0);
-    EXPECT_EQ(sem->ConstantValue().Element<AFloat>(2).value, 0.0);
+    auto* mat = sem->Type()->As<sem::Matrix>();
+    ASSERT_NE(mat, nullptr);
+    EXPECT_TRUE(mat->type()->Is<sem::F16>());
+    EXPECT_EQ(mat->columns(), 2u);
+    EXPECT_EQ(mat->rows(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f16>(), 0._h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f16>(), 0._h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f16>(), 0._h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f16>(), 0._h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f16>(), 0._h);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f16>(), 0._h);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Construct_Scalars_af) {
+    auto* expr = Construct(ty.mat(nullptr, 3, 2), 1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* mat = sem->Type()->As<sem::Matrix>();
+    ASSERT_NE(mat, nullptr);
+    EXPECT_TRUE(mat->type()->Is<sem::F32>());
+    EXPECT_EQ(mat->columns(), 3u);
+    EXPECT_EQ(mat->rows(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<AFloat>(), 1._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<AFloat>(), 2._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<AFloat>(), 3._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<AFloat>(), 4._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<AFloat>(), 5._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<AFloat>(), 6._a);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Construct_Columns_af) {
+    auto* expr = Construct(ty.mat(nullptr, 3, 2),           //
+                           vec(nullptr, 2u, 1.0_a, 2.0_a),  //
+                           vec(nullptr, 2u, 3.0_a, 4.0_a),  //
+                           vec(nullptr, 2u, 5.0_a, 6.0_a));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* mat = sem->Type()->As<sem::Matrix>();
+    ASSERT_NE(mat, nullptr);
+    EXPECT_TRUE(mat->type()->Is<sem::F32>());
+    EXPECT_EQ(mat->columns(), 3u);
+    EXPECT_EQ(mat->rows(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<AFloat>(), 1._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<AFloat>(), 2._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<AFloat>(), 3._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<AFloat>(), 4._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(0)->As<AFloat>(), 5._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->Index(1)->As<AFloat>(), 6._a);
+}
+
+TEST_F(ResolverConstantsTest, Array_i32_Zero) {
+    auto* expr = Construct(ty.array<i32, 4>());
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* arr = sem->Type()->As<sem::Array>();
+    ASSERT_NE(arr, nullptr);
+    EXPECT_TRUE(arr->ElemType()->Is<sem::I32>());
+    EXPECT_EQ(arr->Count(), 4u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 0_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 0_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 0_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(3)->As<i32>(), 0_i);
+}
+
+TEST_F(ResolverConstantsTest, Array_f32_Zero) {
+    auto* expr = Construct(ty.array<f32, 4>());
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* arr = sem->Type()->As<sem::Array>();
+    ASSERT_NE(arr, nullptr);
+    EXPECT_TRUE(arr->ElemType()->Is<sem::F32>());
+    EXPECT_EQ(arr->Count(), 4u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstantsTest, Array_vec3_f32_Zero) {
+    auto* expr = Construct(ty.array(ty.vec3<f32>(), 2_u));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* arr = sem->Type()->As<sem::Array>();
+    ASSERT_NE(arr, nullptr);
+    EXPECT_TRUE(arr->ElemType()->Is<sem::Vector>());
+    EXPECT_EQ(arr->Count(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 0_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllEqual());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AnyZero());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 0_f);
+}
+
+TEST_F(ResolverConstantsTest, Array_i32_Elements) {
+    auto* expr = Construct(ty.array<i32, 4>(), 10_i, 20_i, 30_i, 40_i);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* arr = sem->Type()->As<sem::Array>();
+    ASSERT_NE(arr, nullptr);
+    EXPECT_TRUE(arr->ElemType()->Is<sem::I32>());
+    EXPECT_EQ(arr->Count(), 4u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<i32>(), 10_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<i32>(), 20_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<i32>(), 30_i);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(3)->As<i32>(), 40_i);
+}
+
+TEST_F(ResolverConstantsTest, Array_f32_Elements) {
+    auto* expr = Construct(ty.array<f32, 4>(), 10_f, 20_f, 30_f, 40_f);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* arr = sem->Type()->As<sem::Array>();
+    ASSERT_NE(arr, nullptr);
+    EXPECT_TRUE(arr->ElemType()->Is<sem::F32>());
+    EXPECT_EQ(arr->Count(), 4u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 10_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 20_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 30_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(3)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(3)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(3)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(3)->As<f32>(), 40_f);
+}
+
+TEST_F(ResolverConstantsTest, Array_vec3_f32_Elements) {
+    auto* expr = Construct(ty.array(ty.vec3<f32>(), 2_u),  //
+                           vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* arr = sem->Type()->As<sem::Array>();
+    ASSERT_NE(arr, nullptr);
+    EXPECT_TRUE(arr->ElemType()->Is<sem::Vector>());
+    EXPECT_EQ(arr->Count(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<f32>(), 4_f);
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<f32>(), 5_f);
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Indexing
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ResolverConstantsTest, Vec3_Index) {
+    auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), 2_i);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<i32>(), 3_i);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Index_OOB_High) {
+    auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, 3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index 3 out of bounds [0..2]. Clamping index to 2");
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<i32>(), 3_i);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Index_OOB_Low) {
+    auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, -3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index -3 out of bounds [0..2]. Clamping index to 0");
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->As<i32>(), 1_i);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Index) {
+    auto* expr = IndexAccessor(
+        mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)), 2_i);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 5._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 6._a);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_High) {
+    auto* expr = IndexAccessor(
+        mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
+        Expr(Source{{12, 34}}, 3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index 3 out of bounds [0..2]. Clamping index to 2");
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 5._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 6._a);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_Low) {
+    auto* expr = IndexAccessor(
+        mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
+        Expr(Source{{12, 34}}, -3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index -3 out of bounds [0..2]. Clamping index to 0");
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 1._a);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 2._a);
+}
+
+TEST_F(ResolverConstantsTest, Array_vec3_f32_Index) {
+    auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u),  //
+                                         vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
+                               1_i);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 4_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 5_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 6_f);
+}
+
+TEST_F(ResolverConstantsTest, Array_vec3_f32_Index_OOB_High) {
+    auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u),  //
+                                         vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
+                               Expr(Source{{12, 34}}, 2_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index 2 out of bounds [0..1]. Clamping index to 1");
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 4_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 5_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 6_f);
+}
+
+TEST_F(ResolverConstantsTest, Array_vec3_f32_Index_OOB_Low) {
+    auto* expr = IndexAccessor(Construct(ty.array(ty.vec3<f32>(), 2_u),  //
+                                         vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(4_f, 5_f, 6_f)),
+                               Expr(Source{{12, 34}}, -2_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index -2 out of bounds [0..1]. Clamping index to 0");
+
+    auto* sem = Sem().Get(expr);
+    ASSERT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_TRUE(vec->type()->Is<sem::F32>());
+    EXPECT_EQ(vec->Width(), 3u);
+    EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+    EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 1_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 2_f);
+
+    EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+    EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+    EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 3_f);
+}
+
+TEST_F(ResolverConstantsTest, ChainedIndex) {
+    auto* arr_expr = Construct(ty.array(ty.mat2x3<f32>(), 2_u),        // array<mat2x3<f32>, 2u>
+                               mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f),   //
+                                           vec3<f32>(4_f, 5_f, 6_f)),  //
+                               mat2x3<f32>(vec3<f32>(7_f, 0_f, 9_f),   //
+                                           vec3<f32>(10_f, 11_f, 12_f)));
+
+    auto* mat_expr = IndexAccessor(arr_expr, 1_i);  // arr[1]
+    auto* vec_expr = IndexAccessor(mat_expr, 0_i);  // arr[1][0]
+    auto* f32_expr = IndexAccessor(vec_expr, 2_i);  // arr[1][0][2]
+    WrapInFunction(f32_expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    {
+        auto* mat = Sem().Get(mat_expr);
+        EXPECT_NE(mat, nullptr);
+        auto* ty = mat->Type()->As<sem::Matrix>();
+        ASSERT_NE(mat->Type(), nullptr);
+        EXPECT_TRUE(ty->ColumnType()->Is<sem::Vector>());
+        EXPECT_EQ(ty->columns(), 2u);
+        EXPECT_EQ(ty->rows(), 3u);
+        EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type());
+        EXPECT_FALSE(mat->ConstantValue()->AllEqual());
+        EXPECT_TRUE(mat->ConstantValue()->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->AllZero());
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As<f32>(), 7_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual());
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero());
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As<f32>(), 0_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As<f32>(), 9_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As<f32>(), 10_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As<f32>(), 11_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As<f32>(), 12_f);
+    }
+    {
+        auto* vec = Sem().Get(vec_expr);
+        EXPECT_NE(vec, nullptr);
+        auto* ty = vec->Type()->As<sem::Vector>();
+        ASSERT_NE(vec->Type(), nullptr);
+        EXPECT_TRUE(ty->type()->Is<sem::F32>());
+        EXPECT_EQ(ty->Width(), 3u);
+        EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type());
+        EXPECT_FALSE(vec->ConstantValue()->AllEqual());
+        EXPECT_TRUE(vec->ConstantValue()->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->AllZero());
+
+        EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual());
+        EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero());
+        EXPECT_EQ(vec->ConstantValue()->Index(0)->As<f32>(), 7_f);
+
+        EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual());
+        EXPECT_TRUE(vec->ConstantValue()->Index(1)->AnyZero());
+        EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllZero());
+        EXPECT_EQ(vec->ConstantValue()->Index(1)->As<f32>(), 0_f);
+
+        EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual());
+        EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero());
+        EXPECT_EQ(vec->ConstantValue()->Index(2)->As<f32>(), 9_f);
+    }
+    {
+        auto* f = Sem().Get(f32_expr);
+        EXPECT_NE(f, nullptr);
+        EXPECT_TRUE(f->Type()->Is<sem::F32>());
+        EXPECT_EQ(f->ConstantValue()->Type(), f->Type());
+        EXPECT_TRUE(f->ConstantValue()->AllEqual());
+        EXPECT_FALSE(f->ConstantValue()->AnyZero());
+        EXPECT_FALSE(f->ConstantValue()->AllZero());
+        EXPECT_EQ(f->ConstantValue()->As<f32>(), 9_f);
+    }
+}
+
+TEST_F(ResolverConstantsTest, ChainedIndex_OOB) {
+    auto* arr_expr = Construct(ty.array(ty.mat2x3<f32>(), 2_u),        // array<mat2x3<f32>, 2u>
+                               mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f),   //
+                                           vec3<f32>(4_f, 5_f, 6_f)),  //
+                               mat2x3<f32>(vec3<f32>(7_f, 8_f, 9_f),   //
+                                           vec3<f32>(10_f, 11_f, 12_f)));
+
+    auto* mat_expr = IndexAccessor(arr_expr, Expr(Source{{1, 2}}, -3_i));  // arr[3]
+    auto* vec_expr = IndexAccessor(mat_expr, Expr(Source{{3, 4}}, -2_i));  // arr[3][-2]
+    auto* f32_expr = IndexAccessor(vec_expr, Expr(Source{{5, 6}}, 4_i));   // arr[3][-2][4]
+    WrapInFunction(f32_expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), R"(1:2 warning: index -3 out of bounds [0..1]. Clamping index to 0
+3:4 warning: index -2 out of bounds [0..1]. Clamping index to 0
+5:6 warning: index 4 out of bounds [0..2]. Clamping index to 2)");
+
+    {
+        auto* mat = Sem().Get(mat_expr);
+        EXPECT_NE(mat, nullptr);
+        auto* ty = mat->Type()->As<sem::Matrix>();
+        ASSERT_NE(mat->Type(), nullptr);
+        EXPECT_TRUE(ty->ColumnType()->Is<sem::Vector>());
+        EXPECT_EQ(ty->columns(), 2u);
+        EXPECT_EQ(ty->rows(), 3u);
+        EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type());
+        EXPECT_FALSE(mat->ConstantValue()->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->AllZero());
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(1)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As<f32>(), 4_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As<f32>(), 5_f);
+
+        EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero());
+        EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero());
+        EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
+    }
+    {
+        auto* vec = Sem().Get(vec_expr);
+        EXPECT_NE(vec, nullptr);
+        auto* ty = vec->Type()->As<sem::Vector>();
+        ASSERT_NE(vec->Type(), nullptr);
+        EXPECT_TRUE(ty->type()->Is<sem::F32>());
+        EXPECT_EQ(ty->Width(), 3u);
+        EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type());
+        EXPECT_FALSE(vec->ConstantValue()->AllEqual());
+        EXPECT_FALSE(vec->ConstantValue()->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->AllZero());
+
+        EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual());
+        EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero());
+        EXPECT_EQ(vec->ConstantValue()->Index(0)->As<f32>(), 1_f);
+
+        EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual());
+        EXPECT_FALSE(vec->ConstantValue()->Index(1)->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->Index(1)->AllZero());
+        EXPECT_EQ(vec->ConstantValue()->Index(1)->As<f32>(), 2_f);
+
+        EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual());
+        EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero());
+        EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero());
+        EXPECT_EQ(vec->ConstantValue()->Index(2)->As<f32>(), 3_f);
+    }
+    {
+        auto* f = Sem().Get(f32_expr);
+        EXPECT_NE(f, nullptr);
+        EXPECT_TRUE(f->Type()->Is<sem::F32>());
+        EXPECT_EQ(f->ConstantValue()->Type(), f->Type());
+        EXPECT_TRUE(f->ConstantValue()->AllEqual());
+        EXPECT_FALSE(f->ConstantValue()->AnyZero());
+        EXPECT_FALSE(f->ConstantValue()->AllZero());
+        EXPECT_EQ(f->ConstantValue()->As<f32>(), 3_f);
+    }
 }
 
 }  // namespace
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 0fecf0c..9b56c31 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -316,7 +316,7 @@
 
 TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
     auto* init = Expr(2_i);
-    Global("my_var", ty.i32(), ast::StorageClass::kPrivate, init);
+    GlobalVar("my_var", ty.i32(), ast::StorageClass::kPrivate, init);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -424,7 +424,7 @@
 
 TEST_F(ResolverTest, ArraySize_UnsignedLiteral) {
     // var<private> a : array<f32, 10u>;
-    auto* a = Global("a", ty.array(ty.f32(), Expr(10_u)), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr(10_u)), ast::StorageClass::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -437,7 +437,7 @@
 
 TEST_F(ResolverTest, ArraySize_SignedLiteral) {
     // var<private> a : array<f32, 10i>;
-    auto* a = Global("a", ty.array(ty.f32(), Expr(10_i)), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr(10_i)), ast::StorageClass::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -448,11 +448,11 @@
     EXPECT_EQ(ary->Count(), 10u);
 }
 
-TEST_F(ResolverTest, ArraySize_UnsignedConstant) {
-    // let size = 0u;
+TEST_F(ResolverTest, ArraySize_UnsignedConst) {
+    // const size = 10u;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(10_u));
-    auto* a = Global("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -463,11 +463,11 @@
     EXPECT_EQ(ary->Count(), 10u);
 }
 
-TEST_F(ResolverTest, ArraySize_SignedConstant) {
-    // let size = 0;
+TEST_F(ResolverTest, ArraySize_SignedConst) {
+    // const size = 0;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(10_i));
-    auto* a = Global("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -479,7 +479,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Bitcast) {
-    Global("name", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("name", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* bitcast = create<ast::BitcastExpression>(ty.f32(), Expr("name"));
     WrapInFunction(bitcast);
@@ -542,7 +542,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Cast) {
-    Global("name", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("name", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* cast = Construct(ty.f32(), "name");
     WrapInFunction(cast);
@@ -600,7 +600,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
-    auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* my_var = GlobalVar("my_var", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* ident = Expr("my_var");
     WrapInFunction(ident);
@@ -615,7 +615,7 @@
     EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
 }
 
-TEST_F(ResolverTest, Expr_Identifier_GlobalConstant) {
+TEST_F(ResolverTest, Expr_Identifier_GlobalConst) {
     auto* my_var = GlobalConst("my_var", ty.f32(), Construct(ty.f32()));
 
     auto* ident = Expr("my_var");
@@ -776,13 +776,14 @@
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
     auto* s = Structure("S", {Member("m", ty.u32())});
 
-    auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                          ast::AttributeList{
-                              create<ast::BindingAttribute>(0),
-                              create<ast::GroupAttribute>(0),
-                          });
-    auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
-    auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* sb_var =
+        GlobalVar("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                  ast::AttributeList{
+                      create<ast::BindingAttribute>(0u),
+                      create<ast::GroupAttribute>(0u),
+                  });
+    auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
+    auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("my_func", {}, ty.void_(),
                       {
@@ -808,13 +809,14 @@
 TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
     auto* s = Structure("S", {Member("m", ty.u32())});
 
-    auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                          ast::AttributeList{
-                              create<ast::BindingAttribute>(0),
-                              create<ast::GroupAttribute>(0),
-                          });
-    auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
-    auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* sb_var =
+        GlobalVar("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                  ast::AttributeList{
+                      create<ast::BindingAttribute>(0u),
+                      create<ast::GroupAttribute>(0u),
+                  });
+    auto* wg_var = GlobalVar("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
+    auto* priv_var = GlobalVar("priv_var", ty.f32(), ast::StorageClass::kPrivate);
 
     Func("my_func", {}, ty.f32(),
          {Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"), Assign("priv_var", "priv_var"),
@@ -945,10 +947,10 @@
     EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
 }
 
-TEST_F(ResolverTest, Function_WorkgroupSize_Consts) {
-    // let width = 16i;
-    // let height = 8i;
-    // let depth = 2i;
+TEST_F(ResolverTest, Function_WorkgroupSize_ViaConst) {
+    // const width = 16i;
+    // const height = 8i;
+    // const depth = 2i;
     // @compute @workgroup_size(width, height, depth)
     // fn main() {}
     GlobalConst("width", ty.i32(), Expr(16_i));
@@ -973,9 +975,9 @@
     EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
 }
 
-TEST_F(ResolverTest, Function_WorkgroupSize_Consts_NestedInitializer) {
-    // let width = i32(i32(i32(8i)));
-    // let height = i32(i32(i32(4i)));
+TEST_F(ResolverTest, Function_WorkgroupSize_ViaConst_NestedInitializer) {
+    // const width = i32(i32(i32(8i)));
+    // const height = i32(i32(i32(4i)));
     // @compute @workgroup_size(width, height)
     // fn main() {}
     GlobalConst("width", ty.i32(),
@@ -1059,7 +1061,7 @@
 
 TEST_F(ResolverTest, Function_WorkgroupSize_Mixed) {
     // @id(1) override height = 2i;
-    // let depth = 3i;
+    // const depth = 3i;
     // @compute @workgroup_size(8, height, depth)
     // fn main() {}
     auto* height = Override("height", ty.i32(), Expr(2_i), {Id(0)});
@@ -1086,7 +1088,7 @@
 TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
     auto* st =
         Structure("S", {Member("first_member", ty.i32()), Member("second_member", ty.f32())});
-    Global("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
+    GlobalVar("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
 
     auto* mem = MemberAccessor("my_struct", "second_member");
     WrapInFunction(mem);
@@ -1101,6 +1103,7 @@
     auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
     ASSERT_NE(sma, nullptr);
     EXPECT_TRUE(sma->Member()->Type()->Is<sem::F32>());
+    EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
     EXPECT_EQ(sma->Member()->Index(), 1u);
     EXPECT_EQ(sma->Member()->Declaration()->symbol, Symbols().Get("second_member"));
 }
@@ -1109,7 +1112,7 @@
     auto* st =
         Structure("S", {Member("first_member", ty.i32()), Member("second_member", ty.f32())});
     auto* alias = Alias("alias", ty.Of(st));
-    Global("my_struct", ty.Of(alias), ast::StorageClass::kPrivate);
+    GlobalVar("my_struct", ty.Of(alias), ast::StorageClass::kPrivate);
 
     auto* mem = MemberAccessor("my_struct", "second_member");
     WrapInFunction(mem);
@@ -1123,12 +1126,13 @@
     EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
     auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
     ASSERT_NE(sma, nullptr);
+    EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
     EXPECT_TRUE(sma->Member()->Type()->Is<sem::F32>());
     EXPECT_EQ(sma->Member()->Index(), 1u);
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle) {
-    Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
 
     auto* mem = MemberAccessor("my_vec", "xzyw");
     WrapInFunction(mem);
@@ -1139,12 +1143,14 @@
     ASSERT_TRUE(TypeOf(mem)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(mem)->As<sem::Vector>()->type()->Is<sem::F32>());
     EXPECT_EQ(TypeOf(mem)->As<sem::Vector>()->Width(), 4u);
-    ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
-    EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(0, 2, 1, 3));
+    auto* sma = Sem().Get(mem)->As<sem::Swizzle>();
+    ASSERT_NE(sma, nullptr);
+    EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
+    EXPECT_THAT(sma->As<sem::Swizzle>()->Indices(), ElementsAre(0, 2, 1, 3));
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
-    Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* mem = MemberAccessor("my_vec", "b");
     WrapInFunction(mem);
@@ -1156,7 +1162,9 @@
 
     auto* ref = TypeOf(mem)->As<sem::Reference>();
     ASSERT_TRUE(ref->StoreType()->Is<sem::F32>());
-    ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
+    auto* sma = Sem().Get(mem)->As<sem::Swizzle>();
+    ASSERT_NE(sma, nullptr);
+    EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
     EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(2));
 }
 
@@ -1178,7 +1186,7 @@
 
     auto* stB = Structure("B", {Member("foo", ty.vec4<f32>())});
     auto* stA = Structure("A", {Member("mem", ty.array(ty.Of(stB), 3_i))});
-    Global("c", ty.Of(stA), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.Of(stA), ast::StorageClass::kPrivate);
 
     auto* mem =
         MemberAccessor(MemberAccessor(IndexAccessor(MemberAccessor("c", "mem"), 0_i), "foo"), "yx");
@@ -1196,7 +1204,7 @@
 TEST_F(ResolverTest, Expr_MemberAccessor_InBinaryOp) {
     auto* st =
         Structure("S", {Member("first_member", ty.f32()), Member("second_member", ty.f32())});
-    Global("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
+    GlobalVar("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
 
     auto* expr = Add(MemberAccessor("my_struct", "first_member"),
                      MemberAccessor("my_struct", "second_member"));
@@ -1499,8 +1507,8 @@
     ss << FriendlyName(lhs_type) << " " << params.op << " " << FriendlyName(rhs_type);
     SCOPED_TRACE(ss.str());
 
-    Global("lhs", lhs_type, ast::StorageClass::kPrivate);
-    Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1534,8 +1542,8 @@
        << FriendlyName(rhs_type);
     SCOPED_TRACE(ss.str());
 
-    Global("lhs", lhs_type, ast::StorageClass::kPrivate);
-    Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1580,8 +1588,8 @@
     ss << FriendlyName(lhs_type) << " " << op << " " << FriendlyName(rhs_type);
     SCOPED_TRACE(ss.str());
 
-    Global("lhs", lhs_type, ast::StorageClass::kPrivate);
-    Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, op, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1620,8 +1628,8 @@
         is_valid_expr = vec_size == mat_cols;
     }
 
-    Global("lhs", lhs_type, ast::StorageClass::kPrivate);
-    Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
 
     auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1657,8 +1665,8 @@
     auto* col = create<sem::Vector>(f32, lhs_mat_rows);
     auto* result_type = create<sem::Matrix>(col, rhs_mat_cols);
 
-    Global("lhs", lhs_type, ast::StorageClass::kPrivate);
-    Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("lhs", lhs_type, ast::StorageClass::kPrivate);
+    GlobalVar("rhs", rhs_type, ast::StorageClass::kPrivate);
 
     auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -1686,11 +1694,11 @@
     auto op = GetParam();
 
     if (op == ast::UnaryOp::kNot) {
-        Global("ident", ty.vec4<bool>(), ast::StorageClass::kPrivate);
+        GlobalVar("ident", ty.vec4<bool>(), ast::StorageClass::kPrivate);
     } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
-        Global("ident", ty.vec4<i32>(), ast::StorageClass::kPrivate);
+        GlobalVar("ident", ty.vec4<i32>(), ast::StorageClass::kPrivate);
     } else {
-        Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+        GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     }
     auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
     WrapInFunction(der);
@@ -1727,11 +1735,11 @@
 
 TEST_F(ResolverTest, StorageClass_SetForSampler) {
     auto* t = ty.sampler(ast::SamplerKind::kSampler);
-    auto* var = Global("var", t,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(0),
-                       });
+    auto* var = GlobalVar("var", t,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(0u),
+                          });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1740,11 +1748,11 @@
 
 TEST_F(ResolverTest, StorageClass_SetForTexture) {
     auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
-    auto* var = Global("var", t,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(0),
-                       });
+    auto* var = GlobalVar("var", t,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(0u),
+                          });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1765,11 +1773,11 @@
     // struct S { x : i32 };
     // var<storage> g : S;
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
-    auto* var = Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(0),
-                       });
+    auto* var = GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(0u),
+                          });
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1779,12 +1787,12 @@
 TEST_F(ResolverTest, BindingPoint_SetForResources) {
     // @group(1) @binding(2) var s1 : sampler;
     // @group(3) @binding(4) var s2 : sampler;
-    auto* s1 = Global(
+    auto* s1 = GlobalVar(
         Sym(), ty.sampler(ast::SamplerKind::kSampler),
-        ast::AttributeList{create<ast::GroupAttribute>(1), create<ast::BindingAttribute>(2)});
-    auto* s2 = Global(
+        ast::AttributeList{create<ast::GroupAttribute>(1u), create<ast::BindingAttribute>(2u)});
+    auto* s2 = GlobalVar(
         Sym(), ty.sampler(ast::SamplerKind::kSampler),
-        ast::AttributeList{create<ast::GroupAttribute>(3), create<ast::BindingAttribute>(4)});
+        ast::AttributeList{create<ast::GroupAttribute>(3u), create<ast::BindingAttribute>(4u)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1805,11 +1813,11 @@
     // ep_1 -> {}
     // ep_2 -> {}
 
-    Global("first", ty.f32(), ast::StorageClass::kPrivate);
-    Global("second", ty.f32(), ast::StorageClass::kPrivate);
-    Global("call_a", ty.f32(), ast::StorageClass::kPrivate);
-    Global("call_b", ty.f32(), ast::StorageClass::kPrivate);
-    Global("call_c", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("first", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("second", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("call_a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("call_b", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("call_c", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* func_b = Func("b", {}, ty.f32(),
                         {
@@ -1949,8 +1957,8 @@
         {
             ProgramBuilder b;
             auto* expr = b.Expr(1_i);
-            b.Global("a", b.ty.i32(), ast::StorageClass::kPrivate, expr);
-            b.Global("b", b.ty.i32(), ast::StorageClass::kPrivate, expr);
+            b.GlobalVar("a", b.ty.i32(), ast::StorageClass::kPrivate, expr);
+            b.GlobalVar("b", b.ty.i32(), ast::StorageClass::kPrivate, expr);
             Resolver(&b).Resolve();
         },
         "internal compiler error: AST node 'tint::ast::IntLiteralExpression' was encountered twice "
@@ -1958,7 +1966,7 @@
 }
 
 TEST_F(ResolverTest, UnaryOp_Not) {
-    Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr(Source{{12, 34}}, "ident"));
     WrapInFunction(der);
 
@@ -1967,7 +1975,7 @@
 }
 
 TEST_F(ResolverTest, UnaryOp_Complement) {
-    Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* der =
         create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr(Source{{12, 34}}, "ident"));
     WrapInFunction(der);
@@ -1977,7 +1985,7 @@
 }
 
 TEST_F(ResolverTest, UnaryOp_Negation) {
-    Global("ident", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("ident", ty.u32(), ast::StorageClass::kPrivate);
     auto* der =
         create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr(Source{{12, 34}}, "ident"));
     WrapInFunction(der);
@@ -1987,8 +1995,8 @@
 }
 
 TEST_F(ResolverTest, TextureSampler_TextureSample) {
-    Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
-    Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
 
     auto* call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* f =
@@ -2004,8 +2012,8 @@
 }
 
 TEST_F(ResolverTest, TextureSampler_TextureSampleInFunction) {
-    Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
-    Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
 
     auto* inner_call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* inner_func = Func("inner_func", {}, ty.void_(), {inner_call});
@@ -2027,8 +2035,8 @@
 }
 
 TEST_F(ResolverTest, TextureSampler_TextureSampleFunctionDiamondSameVariables) {
-    Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
-    Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
 
     auto* inner_call_1 = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* inner_func_1 = Func("inner_func_1", {}, ty.void_(), {inner_call_1});
@@ -2059,9 +2067,11 @@
 }
 
 TEST_F(ResolverTest, TextureSampler_TextureSampleFunctionDiamondDifferentVariables) {
-    Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
-    Global("t2", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 2));
-    Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 3));
+    GlobalVar("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              GroupAndBinding(1, 1));
+    GlobalVar("t2", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+              GroupAndBinding(1, 2));
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 3));
 
     auto* inner_call_1 = CallStmt(Call("textureSample", "t1", "s", vec2<f32>(1_f, 2_f)));
     const ast::Function* inner_func_1 = Func("inner_func_1", {}, ty.void_(), {inner_call_1});
@@ -2094,7 +2104,7 @@
 }
 
 TEST_F(ResolverTest, TextureSampler_TextureDimensions) {
-    Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 2));
+    GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 2));
 
     auto* call = Call("textureDimensions", "t");
     const ast::Function* f = WrapInFunction(call);
@@ -2110,15 +2120,15 @@
 
 TEST_F(ResolverTest, ModuleDependencyOrderedDeclarations) {
     auto* f0 = Func("f0", {}, ty.void_(), {});
-    auto* v0 = Global("v0", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v0 = GlobalVar("v0", ty.i32(), ast::StorageClass::kPrivate);
     auto* a0 = Alias("a0", ty.i32());
     auto* s0 = Structure("s0", {Member("m", ty.i32())});
     auto* f1 = Func("f1", {}, ty.void_(), {});
-    auto* v1 = Global("v1", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v1 = GlobalVar("v1", ty.i32(), ast::StorageClass::kPrivate);
     auto* a1 = Alias("a1", ty.i32());
     auto* s1 = Structure("s1", {Member("m", ty.i32())});
     auto* f2 = Func("f2", {}, ty.void_(), {});
-    auto* v2 = Global("v2", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v2 = GlobalVar("v2", ty.i32(), ast::StorageClass::kPrivate);
     auto* a2 = Alias("a2", ty.i32());
     auto* s2 = Structure("s2", {Member("m", ty.i32())});
 
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index a0d71e5..f56d822 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -519,7 +519,7 @@
     /// @return a new AST expression of the alias type
     static inline const ast::Expression* Expr(ProgramBuilder& b, double /*unused*/) {
         auto sym = b.Symbols().New("global_for_ptr");
-        b.Global(sym, DataType<T>::AST(b), ast::StorageClass::kPrivate);
+        b.GlobalVar(sym, DataType<T>::AST(b), ast::StorageClass::kPrivate);
         return b.AddressOf(sym);
     }
     /// @returns the WGSL name for the type
diff --git a/src/tint/resolver/side_effects_test.cc b/src/tint/resolver/side_effects_test.cc
index a50d904..689c7b8 100644
--- a/src/tint/resolver/side_effects_test.cc
+++ b/src/tint/resolver/side_effects_test.cc
@@ -17,6 +17,7 @@
 #include "gtest/gtest.h"
 #include "src/tint/resolver/resolver_test_helper.h"
 #include "src/tint/sem/expression.h"
+#include "src/tint/sem/index_accessor_expression.h"
 #include "src/tint/sem/member_accessor_expression.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -28,7 +29,7 @@
     template <typename T>
     void MakeSideEffectFunc(const char* name) {
         auto global = Sym();
-        Global(global, ty.Of<T>(), ast::StorageClass::kPrivate);
+        GlobalVar(global, ty.Of<T>(), ast::StorageClass::kPrivate);
         auto local = Sym();
         Func(name, {}, ty.Of<T>(),
              {
@@ -41,7 +42,7 @@
     template <typename MAKE_TYPE_FUNC>
     void MakeSideEffectFunc(const char* name, MAKE_TYPE_FUNC make_type) {
         auto global = Sym();
-        Global(global, make_type(), ast::StorageClass::kPrivate);
+        GlobalVar(global, make_type(), ast::StorageClass::kPrivate);
         auto local = Sym();
         Func(name, {}, make_type(),
              {
@@ -86,7 +87,7 @@
 }
 
 TEST_F(SideEffectsTest, Call_Builtin_NoSE) {
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
     auto* expr = Call("dpdx", "a");
     Func("f", {}, ty.void_(), {Ignore(expr)},
          {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
@@ -112,7 +113,7 @@
 }
 
 TEST_F(SideEffectsTest, Call_Builtin_SE) {
-    Global("a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
     auto* expr = Call("atomicAdd", AddressOf("a"), 1_i);
     WrapInFunction(expr);
 
diff --git a/src/tint/resolver/source_variable_test.cc b/src/tint/resolver/source_variable_test.cc
index cd943f3..2648e93 100644
--- a/src/tint/resolver/source_variable_test.cc
+++ b/src/tint/resolver/source_variable_test.cc
@@ -15,6 +15,7 @@
 #include "src/tint/resolver/resolver.h"
 
 #include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/index_accessor_expression.h"
 #include "src/tint/sem/member_accessor_expression.h"
 
 using namespace tint::number_suffixes;  // NOLINT
@@ -25,7 +26,7 @@
 class ResolverSourceVariableTest : public ResolverTest {};
 
 TEST_F(ResolverSourceVariableTest, GlobalPrivateVar) {
-    auto* a = Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -36,7 +37,7 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalWorkgroupVar) {
-    auto* a = Global("a", ty.f32(), ast::StorageClass::kWorkgroup);
+    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kWorkgroup);
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -47,7 +48,7 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalStorageVar) {
-    auto* a = Global("a", ty.f32(), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
+    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -58,7 +59,7 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalUniformVar) {
-    auto* a = Global("a", ty.f32(), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
     auto* expr = Expr(a);
     WrapInFunction(expr);
 
@@ -69,8 +70,8 @@
 }
 
 TEST_F(ResolverSourceVariableTest, GlobalTextureVar) {
-    auto* a = Global("a", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-                     ast::StorageClass::kNone, GroupAndBinding(0, 0));
+    auto* a = GlobalVar("a", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+                        ast::StorageClass::kNone, GroupAndBinding(0, 0));
     auto* expr = Expr(a);
     WrapInFunction(Call("textureDimensions", expr));
 
@@ -196,7 +197,7 @@
     // {
     //   a[2i]
     // }
-    auto* a = Global("a", ty.array(ty.f32(), 4_u), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.array(ty.f32(), 4_u), ast::StorageClass::kPrivate);
     auto* expr = IndexAccessor(a, 2_i);
     WrapInFunction(expr);
 
@@ -213,7 +214,7 @@
     //   a.f
     // }
     auto* S = Structure("S", {Member("f", ty.f32())});
-    auto* a = Global("a", ty.Of(S), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.Of(S), ast::StorageClass::kPrivate);
     auto* expr = MemberAccessor(a, "f");
     WrapInFunction(expr);
 
@@ -229,7 +230,7 @@
     //   let a_ptr1 = &*&a;
     //   let a_ptr2 = &*a_ptr1;
     // }
-    auto* a = Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
     auto* address_of_1 = AddressOf(a);
     auto* deref_1 = Deref(address_of_1);
     auto* address_of_2 = AddressOf(deref_1);
diff --git a/src/tint/resolver/storage_class_layout_validation_test.cc b/src/tint/resolver/storage_class_layout_validation_test.cc
index db379a0..770361a 100644
--- a/src/tint/resolver/storage_class_layout_validation_test.cc
+++ b/src/tint/resolver/storage_class_layout_validation_test.cc
@@ -37,8 +37,8 @@
               {Member("a", ty.f32(), {MemberSize(5)}),
                Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(1)})});
 
-    Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -65,8 +65,8 @@
               {Member("a", ty.f32(), {MemberSize(5)}),
                Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(4)})});
 
-    Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
+              GroupAndBinding(0, 0));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -93,8 +93,8 @@
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -134,8 +134,8 @@
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner"), {MemberAlign(16)}),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -159,8 +159,8 @@
                   Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -192,8 +192,8 @@
                   Member(Source{{34, 56}}, "inner", ty.type_name("Inner"), {MemberAlign(16)}),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -222,8 +222,8 @@
                   Member(Source{{78, 90}}, "scalar", ty.i32()),
               });
 
-    Global(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -274,8 +274,8 @@
                   Member(Source{{78, 90}}, "scalar", ty.i32()),
               });
 
-    Global(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -320,8 +320,8 @@
                   Member(Source{{78, 90}}, "scalar", ty.i32(), {MemberAlign(16)}),
               });
 
-    Global(Source{{22, 34}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{22, 34}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -341,8 +341,8 @@
                                              Member("s", ty.f32()),
                                          });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
-           ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
+              ast::StorageClass::kUniform, GroupAndBinding(0, 0));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -367,8 +367,8 @@
                   Member("scalar", ty.i32()),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -401,8 +401,8 @@
                   Member("scalar", ty.i32()),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -444,8 +444,8 @@
                   Member("scalar", ty.i32()),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -462,8 +462,8 @@
 TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
     // @group(0) @binding(0)
     // var<uniform> a : array<f32, 4u>;
-    Global(Source{{78, 90}}, "a", ty.array(Source{{34, 56}}, ty.f32(), 4_u),
-           ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.array(Source{{34, 56}}, ty.f32(), 4_u),
+              ast::StorageClass::kUniform, GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -484,8 +484,8 @@
                   Member("inner", ty.array(Source{{34, 56}}, ty.array(ty.f32(), 4_u), 4_u)),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(
@@ -517,8 +517,8 @@
                   Member("scalar", ty.i32()),
               });
 
-    Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
-           GroupAndBinding(0, 0));
+    GlobalVar(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+              GroupAndBinding(0, 0));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/storage_class_validation_test.cc
index a5e7d12..0e2be29 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/storage_class_validation_test.cc
@@ -27,7 +27,7 @@
 
 TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
     // var g : f32;
-    Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
+    GlobalVar(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -36,7 +36,7 @@
 
 TEST_F(ResolverStorageClassValidationTest, GlobalVariableFunctionStorageClass_Fail) {
     // var<function> g : f32;
-    Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kFunction);
+    GlobalVar(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kFunction);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -44,96 +44,96 @@
 }
 
 TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArray) {
-    Global(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
-12:34 note: while instantiating variable v)");
+12:34 note: while instantiating 'var' v)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArrayInStruct) {
     auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
-    Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
 note: while analysing structure member S.m
-12:34 note: while instantiating variable v)");
+12:34 note: while instantiating 'var' v)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArray) {
-    Global(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
-12:34 note: while instantiating variable v)");
+12:34 note: while instantiating 'var' v)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArrayInStruct) {
     auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
-    Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
+    GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
 note: while analysing structure member S.m
-12:34 note: while instantiating variable v)");
+12:34 note: while instantiating 'var' v)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
     // var<storage> g : bool;
-    Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
         R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
     // var<storage> g : ptr<private, f32>;
-    Global(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
-           ast::StorageClass::kStorage,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
+              ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
         R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'storage' as it is non-host-shareable
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferIntScalar) {
     // var<storage> g : i32;
-    Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverStorageClassValidationTest, StorageBufferVector) {
     // var<storage> g : vec4<f32>;
-    Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -142,11 +142,11 @@
     // var<storage, read> g : array<S, 3u>;
     auto* s = Structure("S", {Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
-    Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -155,23 +155,23 @@
     // type a = bool;
     // var<storage, read> g : a;
     auto* a = Alias("a", ty.bool_());
-    Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
         R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
     // var<private, read> g : a;
-    Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate, ast::Access::kRead);
+    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate, ast::Access::kRead);
 
     ASSERT_FALSE(r()->Resolve());
 
@@ -184,11 +184,11 @@
     // struct S { x : i32 };
     // var<storage, read> g : S;
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
-    Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve());
 }
@@ -200,11 +200,11 @@
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
     auto* a1 = Alias("a1", ty.Of(s));
     auto* a2 = Alias("a2", ty.Of(a1));
-    Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve());
 }
@@ -215,70 +215,70 @@
 
     auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())});
 
-    Global(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
 note: while analysing structure member S.m
-56:78 note: while instantiating variable svar)");
+56:78 note: while instantiating 'var' svar)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
     // var<uniform> g : bool;
-    Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
         R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
     // var<uniform> g : ptr<private, f32>;
-    Global(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
-           ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
+              ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
         R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'uniform' as it is non-host-shareable
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferIntScalar) {
     // var<uniform> g : i32;
-    Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferVector) {
     // var<uniform> g : vec4<f32>;
-    Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -290,11 +290,11 @@
     // var<uniform> g : array<S, 3u>;
     auto* s = Structure("S", {Member("a", ty.f32(), {MemberSize(16)})});
     auto* a = ty.array(ty.Of(s), 3_u);
-    Global(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -303,29 +303,29 @@
     // type a = bool;
     // var<uniform> g : a;
     auto* a = Alias("a", ty.bool_());
-    Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(
         r()->error(),
         R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
     // struct S { x : i32 };
     // var<uniform> g :  S;
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
-    Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -336,11 +336,11 @@
     // var<uniform> g : a1;
     auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
     auto* a1 = Alias("a1", ty.Of(s));
-    Global(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/tint/resolver/struct_layout_test.cc b/src/tint/resolver/struct_layout_test.cc
index 854e87a..73ecd4e 100644
--- a/src/tint/resolver/struct_layout_test.cc
+++ b/src/tint/resolver/struct_layout_test.cc
@@ -49,6 +49,60 @@
     EXPECT_EQ(sem->Members()[2]->Offset(), 8u);
     EXPECT_EQ(sem->Members()[2]->Align(), 4u);
     EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
+}
+
+TEST_F(ResolverStructLayoutTest, ScalarsWithF16) {
+    Enable(ast::Extension::kF16);
+
+    auto* s = Structure("S", {
+                                 Member("a", ty.f32()),
+                                 Member("b", ty.f16()),
+                                 Member("c", ty.u32()),
+                                 Member("d", ty.f16()),
+                                 Member("e", ty.f16()),
+                                 Member("f", ty.i32()),
+                                 Member("g", ty.f16()),
+                             });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = TypeOf(s)->As<sem::Struct>();
+    ASSERT_NE(sem, nullptr);
+    EXPECT_EQ(sem->Size(), 24u);
+    EXPECT_EQ(sem->SizeNoPadding(), 22u);
+    EXPECT_EQ(sem->Align(), 4u);
+    ASSERT_EQ(sem->Members().size(), 7u);
+    // f32
+    EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+    EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+    // f16
+    EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
+    EXPECT_EQ(sem->Members()[1]->Align(), 2u);
+    EXPECT_EQ(sem->Members()[1]->Size(), 2u);
+    // u32
+    EXPECT_EQ(sem->Members()[2]->Offset(), 8u);
+    EXPECT_EQ(sem->Members()[2]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+    // f16
+    EXPECT_EQ(sem->Members()[3]->Offset(), 12u);
+    EXPECT_EQ(sem->Members()[3]->Align(), 2u);
+    EXPECT_EQ(sem->Members()[3]->Size(), 2u);
+    // f16
+    EXPECT_EQ(sem->Members()[4]->Offset(), 14u);
+    EXPECT_EQ(sem->Members()[4]->Align(), 2u);
+    EXPECT_EQ(sem->Members()[4]->Size(), 2u);
+    // i32
+    EXPECT_EQ(sem->Members()[5]->Offset(), 16u);
+    EXPECT_EQ(sem->Members()[5]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[5]->Size(), 4u);
+    // f16
+    EXPECT_EQ(sem->Members()[6]->Offset(), 20u);
+    EXPECT_EQ(sem->Members()[6]->Align(), 2u);
+    EXPECT_EQ(sem->Members()[6]->Size(), 2u);
 }
 
 TEST_F(ResolverStructLayoutTest, Alias) {
@@ -74,58 +128,89 @@
     EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
     EXPECT_EQ(sem->Members()[1]->Align(), 4u);
     EXPECT_EQ(sem->Members()[1]->Size(), 4u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) {
+    Enable(ast::Extension::kF16);
+
     auto* s = Structure("S", {
                                  Member("a", ty.array<i32, 3>()),
                                  Member("b", ty.array<f32, 5>()),
-                                 Member("c", ty.array<f32, 1>()),
+                                 Member("c", ty.array<f16, 7>()),
+                                 Member("d", ty.array<f32, 1>()),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_EQ(sem->Size(), 36u);
-    EXPECT_EQ(sem->SizeNoPadding(), 36u);
+    EXPECT_EQ(sem->Size(), 52u);
+    EXPECT_EQ(sem->SizeNoPadding(), 52u);
     EXPECT_EQ(sem->Align(), 4u);
-    ASSERT_EQ(sem->Members().size(), 3u);
+    ASSERT_EQ(sem->Members().size(), 4u);
+    // array<i32, 3>
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 4u);
     EXPECT_EQ(sem->Members()[0]->Size(), 12u);
+    // array<f32, 5>
     EXPECT_EQ(sem->Members()[1]->Offset(), 12u);
     EXPECT_EQ(sem->Members()[1]->Align(), 4u);
     EXPECT_EQ(sem->Members()[1]->Size(), 20u);
+    // array<f16, 7>
     EXPECT_EQ(sem->Members()[2]->Offset(), 32u);
-    EXPECT_EQ(sem->Members()[2]->Align(), 4u);
-    EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+    EXPECT_EQ(sem->Members()[2]->Align(), 2u);
+    EXPECT_EQ(sem->Members()[2]->Size(), 14u);
+    // array<f32, 1>
+    EXPECT_EQ(sem->Members()[3]->Offset(), 48u);
+    EXPECT_EQ(sem->Members()[3]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[3]->Size(), 4u);
+
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) {
+    Enable(ast::Extension::kF16);
+
     auto* s = Structure("S", {
                                  Member("a", ty.array<i32, 3>(/*stride*/ 8)),
                                  Member("b", ty.array<f32, 5>(/*stride*/ 16)),
-                                 Member("c", ty.array<f32, 1>(/*stride*/ 32)),
+                                 Member("c", ty.array<f16, 7>(/*stride*/ 4)),
+                                 Member("d", ty.array<f32, 1>(/*stride*/ 32)),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_EQ(sem->Size(), 136u);
-    EXPECT_EQ(sem->SizeNoPadding(), 136u);
+    EXPECT_EQ(sem->Size(), 164u);
+    EXPECT_EQ(sem->SizeNoPadding(), 164u);
     EXPECT_EQ(sem->Align(), 4u);
-    ASSERT_EQ(sem->Members().size(), 3u);
+    ASSERT_EQ(sem->Members().size(), 4u);
+    // array<i32, 3>, stride = 8
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 4u);
     EXPECT_EQ(sem->Members()[0]->Size(), 24u);
+    // array<f32, 5>, stride = 16
     EXPECT_EQ(sem->Members()[1]->Offset(), 24u);
     EXPECT_EQ(sem->Members()[1]->Align(), 4u);
     EXPECT_EQ(sem->Members()[1]->Size(), 80u);
+    // array<f16, 7>, stride = 4
     EXPECT_EQ(sem->Members()[2]->Offset(), 104u);
-    EXPECT_EQ(sem->Members()[2]->Align(), 4u);
-    EXPECT_EQ(sem->Members()[2]->Size(), 32u);
+    EXPECT_EQ(sem->Members()[2]->Align(), 2u);
+    EXPECT_EQ(sem->Members()[2]->Size(), 28u);
+    // array<f32, 1>, stride = 32
+    EXPECT_EQ(sem->Members()[3]->Offset(), 132u);
+    EXPECT_EQ(sem->Members()[3]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[3]->Size(), 32u);
+
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
@@ -144,6 +229,9 @@
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 4u);
     EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
@@ -162,6 +250,9 @@
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 4u);
     EXPECT_EQ(sem->Members()[0]->Size(), 32u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
@@ -182,6 +273,9 @@
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 4u);
     EXPECT_EQ(sem->Members()[0]->Size(), 384u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
@@ -206,6 +300,9 @@
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 16u);
     EXPECT_EQ(sem->Members()[0]->Size(), 576u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, Vector) {
@@ -232,56 +329,101 @@
     EXPECT_EQ(sem->Members()[2]->Offset(), 32u);  // vec4
     EXPECT_EQ(sem->Members()[2]->Align(), 16u);
     EXPECT_EQ(sem->Members()[2]->Size(), 16u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, Matrix) {
+    Enable(ast::Extension::kF16);
+
     auto* s = Structure("S", {
-                                 Member("a", ty.mat2x2<f32>()),
-                                 Member("b", ty.mat2x3<f32>()),
-                                 Member("c", ty.mat2x4<f32>()),
-                                 Member("d", ty.mat3x2<f32>()),
-                                 Member("e", ty.mat3x3<f32>()),
-                                 Member("f", ty.mat3x4<f32>()),
-                                 Member("g", ty.mat4x2<f32>()),
-                                 Member("h", ty.mat4x3<f32>()),
-                                 Member("i", ty.mat4x4<f32>()),
+                                 Member("a_1", ty.mat2x2<f32>()),
+                                 Member("a_2", ty.mat2x2<f16>()),
+                                 Member("b_1", ty.mat2x3<f32>()),
+                                 Member("b_2", ty.mat2x3<f16>()),
+                                 Member("c_1", ty.mat2x4<f32>()),
+                                 Member("c_2", ty.mat2x4<f16>()),
+                                 Member("d_1", ty.mat3x2<f32>()),
+                                 Member("d_2", ty.mat3x2<f16>()),
+                                 Member("e_1", ty.mat3x3<f32>()),
+                                 Member("e_2", ty.mat3x3<f16>()),
+                                 Member("f_1", ty.mat3x4<f32>()),
+                                 Member("f_2", ty.mat3x4<f16>()),
+                                 Member("g_1", ty.mat4x2<f32>()),
+                                 Member("g_2", ty.mat4x2<f16>()),
+                                 Member("h_1", ty.mat4x3<f32>()),
+                                 Member("h_2", ty.mat4x3<f16>()),
+                                 Member("i_1", ty.mat4x4<f32>()),
+                                 Member("i_2", ty.mat4x4<f16>()),
                              });
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
     auto* sem = TypeOf(s)->As<sem::Struct>();
     ASSERT_NE(sem, nullptr);
-    EXPECT_EQ(sem->Size(), 368u);
-    EXPECT_EQ(sem->SizeNoPadding(), 368u);
+    EXPECT_EQ(sem->Size(), 576u);
+    EXPECT_EQ(sem->SizeNoPadding(), 576u);
     EXPECT_EQ(sem->Align(), 16u);
-    ASSERT_EQ(sem->Members().size(), 9u);
-    EXPECT_EQ(sem->Members()[0]->Offset(), 0u);  // mat2x2
+    ASSERT_EQ(sem->Members().size(), 18u);
+    EXPECT_EQ(sem->Members()[0]->Offset(), 0u);  // mat2x2<f32>
     EXPECT_EQ(sem->Members()[0]->Align(), 8u);
     EXPECT_EQ(sem->Members()[0]->Size(), 16u);
-    EXPECT_EQ(sem->Members()[1]->Offset(), 16u);  // mat2x3
-    EXPECT_EQ(sem->Members()[1]->Align(), 16u);
-    EXPECT_EQ(sem->Members()[1]->Size(), 32u);
-    EXPECT_EQ(sem->Members()[2]->Offset(), 48u);  // mat2x4
+    EXPECT_EQ(sem->Members()[1]->Offset(), 16u);  // mat2x2<f16>
+    EXPECT_EQ(sem->Members()[1]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[1]->Size(), 8u);
+    EXPECT_EQ(sem->Members()[2]->Offset(), 32u);  // mat2x3<f32>
     EXPECT_EQ(sem->Members()[2]->Align(), 16u);
     EXPECT_EQ(sem->Members()[2]->Size(), 32u);
-    EXPECT_EQ(sem->Members()[3]->Offset(), 80u);  // mat3x2
+    EXPECT_EQ(sem->Members()[3]->Offset(), 64u);  // mat2x3<f16>
     EXPECT_EQ(sem->Members()[3]->Align(), 8u);
-    EXPECT_EQ(sem->Members()[3]->Size(), 24u);
-    EXPECT_EQ(sem->Members()[4]->Offset(), 112u);  // mat3x3
+    EXPECT_EQ(sem->Members()[3]->Size(), 16u);
+    EXPECT_EQ(sem->Members()[4]->Offset(), 80u);  // mat2x4<f32>
     EXPECT_EQ(sem->Members()[4]->Align(), 16u);
-    EXPECT_EQ(sem->Members()[4]->Size(), 48u);
-    EXPECT_EQ(sem->Members()[5]->Offset(), 160u);  // mat3x4
-    EXPECT_EQ(sem->Members()[5]->Align(), 16u);
-    EXPECT_EQ(sem->Members()[5]->Size(), 48u);
-    EXPECT_EQ(sem->Members()[6]->Offset(), 208u);  // mat4x2
+    EXPECT_EQ(sem->Members()[4]->Size(), 32u);
+    EXPECT_EQ(sem->Members()[5]->Offset(), 112u);  // mat2x4<f16>
+    EXPECT_EQ(sem->Members()[5]->Align(), 8u);
+    EXPECT_EQ(sem->Members()[5]->Size(), 16u);
+    EXPECT_EQ(sem->Members()[6]->Offset(), 128u);  // mat3x2<f32>
     EXPECT_EQ(sem->Members()[6]->Align(), 8u);
-    EXPECT_EQ(sem->Members()[6]->Size(), 32u);
-    EXPECT_EQ(sem->Members()[7]->Offset(), 240u);  // mat4x3
-    EXPECT_EQ(sem->Members()[7]->Align(), 16u);
-    EXPECT_EQ(sem->Members()[7]->Size(), 64u);
-    EXPECT_EQ(sem->Members()[8]->Offset(), 304u);  // mat4x4
+    EXPECT_EQ(sem->Members()[6]->Size(), 24u);
+    EXPECT_EQ(sem->Members()[7]->Offset(), 152u);  // mat3x2<f16>
+    EXPECT_EQ(sem->Members()[7]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[7]->Size(), 12u);
+    EXPECT_EQ(sem->Members()[8]->Offset(), 176u);  // mat3x3<f32>
     EXPECT_EQ(sem->Members()[8]->Align(), 16u);
-    EXPECT_EQ(sem->Members()[8]->Size(), 64u);
+    EXPECT_EQ(sem->Members()[8]->Size(), 48u);
+    EXPECT_EQ(sem->Members()[9]->Offset(), 224u);  // mat3x3<f16>
+    EXPECT_EQ(sem->Members()[9]->Align(), 8u);
+    EXPECT_EQ(sem->Members()[9]->Size(), 24u);
+    EXPECT_EQ(sem->Members()[10]->Offset(), 256u);  // mat3x4<f32>
+    EXPECT_EQ(sem->Members()[10]->Align(), 16u);
+    EXPECT_EQ(sem->Members()[10]->Size(), 48u);
+    EXPECT_EQ(sem->Members()[11]->Offset(), 304u);  // mat3x4<f16>
+    EXPECT_EQ(sem->Members()[11]->Align(), 8u);
+    EXPECT_EQ(sem->Members()[11]->Size(), 24u);
+    EXPECT_EQ(sem->Members()[12]->Offset(), 328u);  // mat4x2<f32>
+    EXPECT_EQ(sem->Members()[12]->Align(), 8u);
+    EXPECT_EQ(sem->Members()[12]->Size(), 32u);
+    EXPECT_EQ(sem->Members()[13]->Offset(), 360u);  // mat4x2<f16>
+    EXPECT_EQ(sem->Members()[13]->Align(), 4u);
+    EXPECT_EQ(sem->Members()[13]->Size(), 16u);
+    EXPECT_EQ(sem->Members()[14]->Offset(), 384u);  // mat4x3<f32>
+    EXPECT_EQ(sem->Members()[14]->Align(), 16u);
+    EXPECT_EQ(sem->Members()[14]->Size(), 64u);
+    EXPECT_EQ(sem->Members()[15]->Offset(), 448u);  // mat4x3<f16>
+    EXPECT_EQ(sem->Members()[15]->Align(), 8u);
+    EXPECT_EQ(sem->Members()[15]->Size(), 32u);
+    EXPECT_EQ(sem->Members()[16]->Offset(), 480u);  // mat4x4<f32>
+    EXPECT_EQ(sem->Members()[16]->Align(), 16u);
+    EXPECT_EQ(sem->Members()[16]->Size(), 64u);
+    EXPECT_EQ(sem->Members()[17]->Offset(), 544u);  // mat4x4<f16>
+    EXPECT_EQ(sem->Members()[17]->Align(), 8u);
+    EXPECT_EQ(sem->Members()[17]->Size(), 32u);
+
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, NestedStruct) {
@@ -311,6 +453,9 @@
     EXPECT_EQ(sem->Members()[2]->Offset(), 64u);
     EXPECT_EQ(sem->Members()[2]->Align(), 4u);
     EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, SizeAttributes) {
@@ -346,6 +491,9 @@
     EXPECT_EQ(sem->Members()[3]->Offset(), 44u);
     EXPECT_EQ(sem->Members()[3]->Align(), 4u);
     EXPECT_EQ(sem->Members()[3]->Size(), 32u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, AlignAttributes) {
@@ -381,6 +529,9 @@
     EXPECT_EQ(sem->Members()[3]->Offset(), 64u);
     EXPECT_EQ(sem->Members()[3]->Align(), 32u);
     EXPECT_EQ(sem->Members()[3]->Size(), 4u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 TEST_F(ResolverStructLayoutTest, StructWithLotsOfPadding) {
@@ -399,6 +550,9 @@
     EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
     EXPECT_EQ(sem->Members()[0]->Align(), 1024u);
     EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+    for (auto& m : sem->Members()) {
+        EXPECT_EQ(m->Struct()->Declaration(), s);
+    }
 }
 
 }  // namespace
diff --git a/src/tint/resolver/struct_storage_class_use_test.cc b/src/tint/resolver/struct_storage_class_use_test.cc
index 72ed546..5a929a5 100644
--- a/src/tint/resolver/struct_storage_class_use_test.cc
+++ b/src/tint/resolver/struct_storage_class_use_test.cc
@@ -64,7 +64,7 @@
 TEST_F(ResolverStorageClassUseTest, StructReachableFromGlobal) {
     auto* s = Structure("S", {Member("a", ty.f32())});
 
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -76,7 +76,7 @@
 TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalAlias) {
     auto* s = Structure("S", {Member("a", ty.f32())});
     auto* a = Alias("A", ty.Of(s));
-    Global("g", ty.Of(a), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(a), ast::StorageClass::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -88,7 +88,7 @@
 TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalStruct) {
     auto* s = Structure("S", {Member("a", ty.f32())});
     auto* o = Structure("O", {Member("a", ty.Of(s))});
-    Global("g", ty.Of(o), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(o), ast::StorageClass::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -100,7 +100,7 @@
 TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) {
     auto* s = Structure("S", {Member("a", ty.f32())});
     auto* a = ty.array(ty.Of(s), 3_u);
-    Global("g", a, ast::StorageClass::kPrivate);
+    GlobalVar("g", a, ast::StorageClass::kPrivate);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -159,16 +159,16 @@
 
 TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
     auto* s = Structure("S", {Member("a", ty.f32())});
-    Global("x", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
-    Global("y", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("x", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
+    GlobalVar("y", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(0u),
+              });
     WrapInFunction(Var("g", ty.Of(s)));
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc
index 3277ee8..1c857cd 100644
--- a/src/tint/resolver/type_constructor_validation_test.cc
+++ b/src/tint/resolver/type_constructor_validation_test.cc
@@ -83,6 +83,8 @@
     // }
     auto& params = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     auto* constructor_expr = params.create_rhs_ast_value(*this, 0);
 
     auto* a = Var("a", nullptr, ast::StorageClass::kNone, constructor_expr);
@@ -104,18 +106,24 @@
     ParamsFor<i32>(),
     ParamsFor<u32>(),
     ParamsFor<f32>(),
+    ParamsFor<f16>(),
     ParamsFor<vec3<i32>>(),
     ParamsFor<vec3<u32>>(),
     ParamsFor<vec3<f32>>(),
+    ParamsFor<vec3<f16>>(),
     ParamsFor<mat3x3<f32>>(),
+    ParamsFor<mat3x3<f16>>(),
     ParamsFor<alias<bool>>(),
     ParamsFor<alias<i32>>(),
     ParamsFor<alias<u32>>(),
     ParamsFor<alias<f32>>(),
+    ParamsFor<alias<f16>>(),
     ParamsFor<alias<vec3<i32>>>(),
     ParamsFor<alias<vec3<u32>>>(),
     ParamsFor<alias<vec3<f32>>>(),
+    ParamsFor<alias<vec3<f16>>>(),
     ParamsFor<alias<mat3x3<f32>>>(),
+    ParamsFor<alias<mat3x3<f16>>>(),
 };
 INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
                          InferTypeTest_FromConstructorExpression,
@@ -176,6 +184,8 @@
     // }
     auto& params = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     Func("foo", {}, params.create_rhs_ast_type(*this),
          {Return(Construct(params.create_rhs_ast_type(*this)))}, {});
 
@@ -197,18 +207,24 @@
     ParamsFor<i32>(),
     ParamsFor<u32>(),
     ParamsFor<f32>(),
+    ParamsFor<f16>(),
     ParamsFor<vec3<i32>>(),
     ParamsFor<vec3<u32>>(),
     ParamsFor<vec3<f32>>(),
+    ParamsFor<vec3<f16>>(),
     ParamsFor<mat3x3<f32>>(),
+    ParamsFor<mat3x3<f16>>(),
     ParamsFor<alias<bool>>(),
     ParamsFor<alias<i32>>(),
     ParamsFor<alias<u32>>(),
     ParamsFor<alias<f32>>(),
+    ParamsFor<alias<f16>>(),
     ParamsFor<alias<vec3<i32>>>(),
     ParamsFor<alias<vec3<u32>>>(),
     ParamsFor<alias<vec3<f32>>>(),
+    ParamsFor<alias<vec3<f16>>>(),
     ParamsFor<alias<mat3x3<f32>>>(),
+    ParamsFor<alias<mat3x3<f16>>>(),
 };
 INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
                          InferTypeTest_FromCallExpression,
@@ -240,19 +256,25 @@
     ParamsFor<i32, i32>(Kind::Construct),                  //
     ParamsFor<u32, u32>(Kind::Construct),                  //
     ParamsFor<f32, f32>(Kind::Construct),                  //
+    ParamsFor<f16, f16>(Kind::Construct),                  //
     ParamsFor<vec3<bool>, vec3<bool>>(Kind::Construct),    //
     ParamsFor<vec3<i32>, vec3<i32>>(Kind::Construct),      //
     ParamsFor<vec3<u32>, vec3<u32>>(Kind::Construct),      //
     ParamsFor<vec3<f32>, vec3<f32>>(Kind::Construct),      //
+    ParamsFor<vec3<f16>, vec3<f16>>(Kind::Construct),      //
     ParamsFor<mat3x3<f32>, mat3x3<f32>>(Kind::Construct),  //
     ParamsFor<mat2x3<f32>, mat2x3<f32>>(Kind::Construct),  //
     ParamsFor<mat3x2<f32>, mat3x2<f32>>(Kind::Construct),  //
+    ParamsFor<mat3x3<f16>, mat3x3<f16>>(Kind::Construct),  //
+    ParamsFor<mat2x3<f16>, mat2x3<f16>>(Kind::Construct),  //
+    ParamsFor<mat3x2<f16>, mat3x2<f16>>(Kind::Construct),  //
 
     // Splat
     ParamsFor<vec3<bool>, bool>(Kind::Construct),  //
     ParamsFor<vec3<i32>, i32>(Kind::Construct),    //
     ParamsFor<vec3<u32>, u32>(Kind::Construct),    //
     ParamsFor<vec3<f32>, f32>(Kind::Construct),    //
+    ParamsFor<vec3<f16>, f16>(Kind::Construct),    //
 
     ParamsFor<mat3x3<f32>, f32>(Kind::Construct),  //
     ParamsFor<mat2x3<f32>, f32>(Kind::Construct),  //
@@ -262,40 +284,68 @@
     ParamsFor<bool, u32>(Kind::Conversion),  //
     ParamsFor<bool, i32>(Kind::Conversion),  //
     ParamsFor<bool, f32>(Kind::Conversion),  //
+    ParamsFor<bool, f16>(Kind::Conversion),  //
 
     ParamsFor<i32, bool>(Kind::Conversion),  //
     ParamsFor<i32, u32>(Kind::Conversion),   //
     ParamsFor<i32, f32>(Kind::Conversion),   //
+    ParamsFor<i32, f16>(Kind::Conversion),   //
 
     ParamsFor<u32, bool>(Kind::Conversion),  //
     ParamsFor<u32, i32>(Kind::Conversion),   //
     ParamsFor<u32, f32>(Kind::Conversion),   //
+    ParamsFor<u32, f16>(Kind::Conversion),   //
 
     ParamsFor<f32, bool>(Kind::Conversion),  //
     ParamsFor<f32, u32>(Kind::Conversion),   //
     ParamsFor<f32, i32>(Kind::Conversion),   //
+    ParamsFor<f32, f16>(Kind::Conversion),   //
+
+    ParamsFor<f16, bool>(Kind::Conversion),  //
+    ParamsFor<f16, u32>(Kind::Conversion),   //
+    ParamsFor<f16, i32>(Kind::Conversion),   //
+    ParamsFor<f16, f32>(Kind::Conversion),   //
 
     ParamsFor<vec3<bool>, vec3<u32>>(Kind::Conversion),  //
     ParamsFor<vec3<bool>, vec3<i32>>(Kind::Conversion),  //
     ParamsFor<vec3<bool>, vec3<f32>>(Kind::Conversion),  //
+    ParamsFor<vec3<bool>, vec3<f16>>(Kind::Conversion),  //
 
     ParamsFor<vec3<i32>, vec3<bool>>(Kind::Conversion),  //
     ParamsFor<vec3<i32>, vec3<u32>>(Kind::Conversion),   //
     ParamsFor<vec3<i32>, vec3<f32>>(Kind::Conversion),   //
+    ParamsFor<vec3<i32>, vec3<f16>>(Kind::Conversion),   //
 
     ParamsFor<vec3<u32>, vec3<bool>>(Kind::Conversion),  //
     ParamsFor<vec3<u32>, vec3<i32>>(Kind::Conversion),   //
     ParamsFor<vec3<u32>, vec3<f32>>(Kind::Conversion),   //
+    ParamsFor<vec3<u32>, vec3<f16>>(Kind::Conversion),   //
 
     ParamsFor<vec3<f32>, vec3<bool>>(Kind::Conversion),  //
     ParamsFor<vec3<f32>, vec3<u32>>(Kind::Conversion),   //
     ParamsFor<vec3<f32>, vec3<i32>>(Kind::Conversion),   //
+    ParamsFor<vec3<f32>, vec3<f16>>(Kind::Conversion),   //
+
+    ParamsFor<vec3<f16>, vec3<bool>>(Kind::Conversion),  //
+    ParamsFor<vec3<f16>, vec3<u32>>(Kind::Conversion),   //
+    ParamsFor<vec3<f16>, vec3<i32>>(Kind::Conversion),   //
+    ParamsFor<vec3<f16>, vec3<f32>>(Kind::Conversion),   //
+
+    ParamsFor<mat3x3<f16>, mat3x3<f32>>(Kind::Conversion),  //
+    ParamsFor<mat2x3<f16>, mat2x3<f32>>(Kind::Conversion),  //
+    ParamsFor<mat3x2<f16>, mat3x2<f32>>(Kind::Conversion),  //
+
+    ParamsFor<mat3x3<f32>, mat3x3<f16>>(Kind::Conversion),  //
+    ParamsFor<mat2x3<f32>, mat2x3<f16>>(Kind::Conversion),  //
+    ParamsFor<mat3x2<f32>, mat3x2<f16>>(Kind::Conversion),  //
 };
 
 using ConversionConstructorValidTest = ResolverTestWithParam<Params>;
 TEST_P(ConversionConstructorValidTest, All) {
     auto& params = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     // var a : <lhs_type1> = <lhs_type2>(<rhs_type>(<rhs_value_expr>));
     auto* lhs_type1 = params.lhs_type(*this);
     auto* lhs_type2 = params.lhs_type(*this);
@@ -348,19 +398,24 @@
     CreatePtrsFor<u32>(),          //
     CreatePtrsFor<i32>(),          //
     CreatePtrsFor<f32>(),          //
+    CreatePtrsFor<f16>(),          //
     CreatePtrsFor<vec3<bool>>(),   //
     CreatePtrsFor<vec3<i32>>(),    //
     CreatePtrsFor<vec3<u32>>(),    //
     CreatePtrsFor<vec3<f32>>(),    //
+    CreatePtrsFor<vec3<f16>>(),    //
     CreatePtrsFor<mat3x3<i32>>(),  //
     CreatePtrsFor<mat3x3<u32>>(),  //
     CreatePtrsFor<mat3x3<f32>>(),  //
+    CreatePtrsFor<mat3x3<f16>>(),  //
     CreatePtrsFor<mat2x3<i32>>(),  //
     CreatePtrsFor<mat2x3<u32>>(),  //
     CreatePtrsFor<mat2x3<f32>>(),  //
+    CreatePtrsFor<mat2x3<f16>>(),  //
     CreatePtrsFor<mat3x2<i32>>(),  //
     CreatePtrsFor<mat3x2<u32>>(),  //
-    CreatePtrsFor<mat3x2<f32>>()   //
+    CreatePtrsFor<mat3x2<f32>>(),  //
+    CreatePtrsFor<mat3x2<f16>>(),  //
 };
 
 using ConversionConstructorInvalidTest = ResolverTestWithParam<std::tuple<CreatePtrs,  // lhs
@@ -395,6 +450,8 @@
        << FriendlyName(rhs_type) << "(<rhs value expr>))";
     SCOPED_TRACE(ss.str());
 
+    Enable(ast::Extension::kF16);
+
     auto* a = Var("a", lhs_type1, ast::StorageClass::kNone,
                   Construct(lhs_type2, Construct(rhs_type, rhs_value_expr)));
 
@@ -581,8 +638,7 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array constructor has too few elements: expected 4, "
-              "found 3");
+              "12:34 error: array constructor has too few elements: expected 4, found 3");
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_TooManyElements) {
@@ -674,6 +730,26 @@
     EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
 }
 
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Construct_f16_Success) {
+    Enable(ast::Extension::kF16);
+
+    auto* expr = Construct<f16>(Expr(1.5_h));
+    WrapInFunction(expr);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_NE(TypeOf(expr), nullptr);
+    ASSERT_TRUE(TypeOf(expr)->Is<sem::F16>());
+
+    auto* call = Sem().Get<sem::Call>(expr);
+    ASSERT_NE(call, nullptr);
+    auto* ctor = call->Target()->As<sem::TypeConstructor>();
+    ASSERT_NE(ctor, nullptr);
+    EXPECT_EQ(call->Type(), ctor->ReturnType());
+    ASSERT_EQ(ctor->Parameters().size(), 1u);
+    EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
+}
+
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_f32_to_i32_Success) {
     auto* expr = Construct<i32>(1.23_f);
     WrapInFunction(expr);
@@ -710,8 +786,30 @@
     EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
 }
 
-TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_u32_to_f32_Success) {
-    auto* expr = Construct<f32>(123_u);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_u32_to_f16_Success) {
+    Enable(ast::Extension::kF16);
+
+    auto* expr = Construct<f16>(123_u);
+    WrapInFunction(expr);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_NE(TypeOf(expr), nullptr);
+    ASSERT_TRUE(TypeOf(expr)->Is<sem::F16>());
+
+    auto* call = Sem().Get<sem::Call>(expr);
+    ASSERT_NE(call, nullptr);
+    auto* ctor = call->Target()->As<sem::TypeConversion>();
+    ASSERT_NE(ctor, nullptr);
+    EXPECT_EQ(call->Type(), ctor->ReturnType());
+    ASSERT_EQ(ctor->Parameters().size(), 1u);
+    EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+}
+
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_f16_to_f32_Success) {
+    Enable(ast::Extension::kF16);
+
+    auto* expr = Construct<f32>(123_h);
     WrapInFunction(expr);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
@@ -725,7 +823,7 @@
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
     ASSERT_EQ(ctor->Parameters().size(), 1u);
-    EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+    EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
 }
 
 }  // namespace ScalarConstructor
@@ -742,6 +840,17 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
+       Expr_Constructor_Vec2F16_Error_ScalarArgumentTypeMismatch) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<f16>(), 1_h, 2_f));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec2<f16>(f16, f32)"));
+}
+
+TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec2U32_Error_ScalarArgumentTypeMismatch) {
     WrapInFunction(Construct(Source{{12, 34}}, ty.vec2<u32>(), 1_u, 2_i));
 
@@ -860,6 +969,29 @@
     EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
 }
 
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2F16_Success_Scalar) {
+    Enable(ast::Extension::kF16);
+
+    auto* tc = vec2<f16>(1_h, 1_h);
+    WrapInFunction(tc);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_NE(TypeOf(tc), nullptr);
+    ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+    EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F16>());
+    EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+
+    auto* call = Sem().Get<sem::Call>(tc);
+    ASSERT_NE(call, nullptr);
+    auto* ctor = call->Target()->As<sem::TypeConstructor>();
+    ASSERT_NE(ctor, nullptr);
+    EXPECT_EQ(call->Type(), ctor->ReturnType());
+    ASSERT_EQ(ctor->Parameters().size(), 2u);
+    EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
+    EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F16>());
+}
+
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2U32_Success_Scalar) {
     auto* tc = vec2<u32>(1_u, 1_u);
     WrapInFunction(tc);
@@ -973,6 +1105,17 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
+       Expr_Constructor_Vec3F16_Error_ScalarArgumentTypeMismatch) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<f16>(), 1_h, 2_h, 3_f));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_THAT(r()->error(),
+                HasSubstr("12:34 error: no matching constructor for vec3<f16>(f16, f16, f32)"));
+}
+
+TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec3U32_Error_ScalarArgumentTypeMismatch) {
     WrapInFunction(Construct(Source{{12, 34}}, ty.vec3<u32>(), 1_u, 2_i, 3_u));
 
@@ -1109,6 +1252,30 @@
     EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::F32>());
 }
 
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3F16_Success_Scalar) {
+    Enable(ast::Extension::kF16);
+
+    auto* tc = vec3<f16>(1_h, 1_h, 1_h);
+    WrapInFunction(tc);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_NE(TypeOf(tc), nullptr);
+    ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+    EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F16>());
+    EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+
+    auto* call = Sem().Get<sem::Call>(tc);
+    ASSERT_NE(call, nullptr);
+    auto* ctor = call->Target()->As<sem::TypeConstructor>();
+    ASSERT_NE(ctor, nullptr);
+    EXPECT_EQ(call->Type(), ctor->ReturnType());
+    ASSERT_EQ(ctor->Parameters().size(), 3u);
+    EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F16>());
+    EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F16>());
+    EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::F16>());
+}
+
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3U32_Success_Scalar) {
     auto* tc = vec3<u32>(1_u, 1_u, 1_u);
     WrapInFunction(tc);
@@ -1268,6 +1435,18 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest,
+       Expr_Constructor_Vec4F16_Error_ScalarArgumentTypeMismatch) {
+    Enable(ast::Extension::kF16);
+
+    WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<f16>(), 1_h, 1_h, 1_f, 1_h));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_THAT(
+        r()->error(),
+        HasSubstr("12:34 error: no matching constructor for vec4<f16>(f16, f16, f32, f16)"));
+}
+
+TEST_F(ResolverTypeConstructorValidationTest,
        Expr_Constructor_Vec4U32_Error_ScalarArgumentTypeMismatch) {
     WrapInFunction(Construct(Source{{12, 34}}, ty.vec4<u32>(), 1_u, 1_u, 1_i, 1_u));
 
@@ -1435,6 +1614,20 @@
     EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
 }
 
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4F16_Success_Scalar) {
+    Enable(ast::Extension::kF16);
+
+    auto* tc = vec4<f16>(1_h, 1_h, 1_h, 1_h);
+    WrapInFunction(tc);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_NE(TypeOf(tc), nullptr);
+    ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+    EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F16>());
+    EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+}
+
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4U32_Success_Scalar) {
     auto* tc = vec4<u32>(1_u, 1_u, 1_u, 1_u);
     WrapInFunction(tc);
@@ -1592,7 +1785,7 @@
 
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_Alias_Argument_Error) {
     auto* alias = Alias("UnsignedInt", ty.u32());
-    Global("uint_var", ty.Of(alias), ast::StorageClass::kPrivate);
+    GlobalVar("uint_var", ty.Of(alias), ast::StorageClass::kPrivate);
 
     auto* tc = Construct(Source{{12, 34}}, ty.vec2<f32>(), "uint_var");
     WrapInFunction(tc);
@@ -1604,8 +1797,8 @@
 TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_Alias_Argument_Success) {
     auto* f32_alias = Alias("Float32", ty.f32());
     auto* vec2_alias = Alias("VectorFloat2", ty.vec2<f32>());
-    Global("my_f32", ty.Of(f32_alias), ast::StorageClass::kPrivate);
-    Global("my_vec2", ty.Of(vec2_alias), ast::StorageClass::kPrivate);
+    GlobalVar("my_f32", ty.Of(f32_alias), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec2", ty.Of(vec2_alias), ast::StorageClass::kPrivate);
 
     auto* tc = vec3<f32>("my_vec2", "my_f32");
     WrapInFunction(tc);
@@ -1661,11 +1854,14 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec2ElementTypeFromScalars) {
-    auto* vec2_bool = Construct(create<ast::Vector>(nullptr, 2), Expr(true), Expr(false));
-    auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1_i), Expr(2_i));
-    auto* vec2_u32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1_u), Expr(2_u));
-    auto* vec2_f32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1_f), Expr(2_f));
-    WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32);
+    Enable(ast::Extension::kF16);
+
+    auto* vec2_bool = Construct(create<ast::Vector>(nullptr, 2u), Expr(true), Expr(false));
+    auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2u), Expr(1_i), Expr(2_i));
+    auto* vec2_u32 = Construct(create<ast::Vector>(nullptr, 2u), Expr(1_u), Expr(2_u));
+    auto* vec2_f32 = Construct(create<ast::Vector>(nullptr, 2u), Expr(1_f), Expr(2_f));
+    auto* vec2_f16 = Construct(create<ast::Vector>(nullptr, 2u), Expr(1_h), Expr(2_h));
+    WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32, vec2_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1673,26 +1869,33 @@
     ASSERT_TRUE(TypeOf(vec2_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec2_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec2_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec2_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec2_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec2_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec2_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec2_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec2_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec2_bool)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_i32)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_u32)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_f32)->As<sem::Vector>()->Width(), 2u);
+    EXPECT_EQ(TypeOf(vec2_f16)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_bool), TypeOf(vec2_bool->target.type));
     EXPECT_EQ(TypeOf(vec2_i32), TypeOf(vec2_i32->target.type));
     EXPECT_EQ(TypeOf(vec2_u32), TypeOf(vec2_u32->target.type));
     EXPECT_EQ(TypeOf(vec2_f32), TypeOf(vec2_f32->target.type));
+    EXPECT_EQ(TypeOf(vec2_f16), TypeOf(vec2_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec2ElementTypeFromVec2) {
-    auto* vec2_bool = Construct(create<ast::Vector>(nullptr, 2), vec2<bool>(true, false));
-    auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2), vec2<i32>(1_i, 2_i));
-    auto* vec2_u32 = Construct(create<ast::Vector>(nullptr, 2), vec2<u32>(1_u, 2_u));
-    auto* vec2_f32 = Construct(create<ast::Vector>(nullptr, 2), vec2<f32>(1_f, 2_f));
-    WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32);
+    Enable(ast::Extension::kF16);
+
+    auto* vec2_bool = Construct(create<ast::Vector>(nullptr, 2u), vec2<bool>(true, false));
+    auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2u), vec2<i32>(1_i, 2_i));
+    auto* vec2_u32 = Construct(create<ast::Vector>(nullptr, 2u), vec2<u32>(1_u, 2_u));
+    auto* vec2_f32 = Construct(create<ast::Vector>(nullptr, 2u), vec2<f32>(1_f, 2_f));
+    auto* vec2_f16 = Construct(create<ast::Vector>(nullptr, 2u), vec2<f16>(1_h, 2_h));
+    WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32, vec2_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1700,27 +1903,34 @@
     ASSERT_TRUE(TypeOf(vec2_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec2_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec2_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec2_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec2_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec2_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec2_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec2_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec2_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec2_bool)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_i32)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_u32)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_f32)->As<sem::Vector>()->Width(), 2u);
+    EXPECT_EQ(TypeOf(vec2_f16)->As<sem::Vector>()->Width(), 2u);
     EXPECT_EQ(TypeOf(vec2_bool), TypeOf(vec2_bool->target.type));
     EXPECT_EQ(TypeOf(vec2_i32), TypeOf(vec2_i32->target.type));
     EXPECT_EQ(TypeOf(vec2_u32), TypeOf(vec2_u32->target.type));
     EXPECT_EQ(TypeOf(vec2_f32), TypeOf(vec2_f32->target.type));
+    EXPECT_EQ(TypeOf(vec2_f16), TypeOf(vec2_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec3ElementTypeFromScalars) {
+    Enable(ast::Extension::kF16);
+
     auto* vec3_bool =
-        Construct(create<ast::Vector>(nullptr, 3), Expr(true), Expr(false), Expr(true));
-    auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1_i), Expr(2_i), Expr(3_i));
-    auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1_u), Expr(2_u), Expr(3_u));
-    auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1_f), Expr(2_f), Expr(3_f));
-    WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
+        Construct(create<ast::Vector>(nullptr, 3u), Expr(true), Expr(false), Expr(true));
+    auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_i), Expr(2_i), Expr(3_i));
+    auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_u), Expr(2_u), Expr(3_u));
+    auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_f), Expr(2_f), Expr(3_f));
+    auto* vec3_f16 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_h), Expr(2_h), Expr(3_h));
+    WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32, vec3_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1728,26 +1938,33 @@
     ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec3_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec3_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
+    EXPECT_EQ(TypeOf(vec3_f16)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
     EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
     EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
     EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
+    EXPECT_EQ(TypeOf(vec3_f16), TypeOf(vec3_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec3ElementTypeFromVec3) {
-    auto* vec3_bool = Construct(create<ast::Vector>(nullptr, 3), vec3<bool>(true, false, true));
-    auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3), vec3<i32>(1_i, 2_i, 3_i));
-    auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3), vec3<u32>(1_u, 2_u, 3_u));
-    auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), vec3<f32>(1_f, 2_f, 3_f));
-    WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
+    Enable(ast::Extension::kF16);
+
+    auto* vec3_bool = Construct(create<ast::Vector>(nullptr, 3u), vec3<bool>(true, false, true));
+    auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3u), vec3<i32>(1_i, 2_i, 3_i));
+    auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3u), vec3<u32>(1_u, 2_u, 3_u));
+    auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3u), vec3<f32>(1_f, 2_f, 3_f));
+    auto* vec3_f16 = Construct(create<ast::Vector>(nullptr, 3u), vec3<f16>(1_h, 2_h, 3_h));
+    WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32, vec3_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1755,27 +1972,34 @@
     ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec3_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec3_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
+    EXPECT_EQ(TypeOf(vec3_f16)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
     EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
     EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
     EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
+    EXPECT_EQ(TypeOf(vec3_f16), TypeOf(vec3_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec3ElementTypeFromScalarAndVec2) {
+    Enable(ast::Extension::kF16);
+
     auto* vec3_bool =
-        Construct(create<ast::Vector>(nullptr, 3), Expr(true), vec2<bool>(false, true));
-    auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1_i), vec2<i32>(2_i, 3_i));
-    auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1_u), vec2<u32>(2_u, 3_u));
-    auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1_f), vec2<f32>(2_f, 3_f));
-    WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
+        Construct(create<ast::Vector>(nullptr, 3u), Expr(true), vec2<bool>(false, true));
+    auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_i), vec2<i32>(2_i, 3_i));
+    auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_u), vec2<u32>(2_u, 3_u));
+    auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_f), vec2<f32>(2_f, 3_f));
+    auto* vec3_f16 = Construct(create<ast::Vector>(nullptr, 3u), Expr(1_h), vec2<f16>(2_h, 3_h));
+    WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32, vec3_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1783,30 +2007,38 @@
     ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec3_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec3_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
+    EXPECT_EQ(TypeOf(vec3_f16)->As<sem::Vector>()->Width(), 3u);
     EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
     EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
     EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
     EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
+    EXPECT_EQ(TypeOf(vec3_f16), TypeOf(vec3_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromScalars) {
-    auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4), Expr(true), Expr(false),
+    Enable(ast::Extension::kF16);
+
+    auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4u), Expr(true), Expr(false),
                                 Expr(true), Expr(false));
     auto* vec4_i32 =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(1_i), Expr(2_i), Expr(3_i), Expr(4_i));
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_i), Expr(2_i), Expr(3_i), Expr(4_i));
     auto* vec4_u32 =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(1_u), Expr(2_u), Expr(3_u), Expr(4_u));
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_u), Expr(2_u), Expr(3_u), Expr(4_u));
     auto* vec4_f32 =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(1_f), Expr(2_f), Expr(3_f), Expr(4_f));
-    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_f), Expr(2_f), Expr(3_f), Expr(4_f));
+    auto* vec4_f16 =
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_h), Expr(2_h), Expr(3_h), Expr(4_h));
+    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32, vec4_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1814,27 +2046,34 @@
     ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec4_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec4_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+    EXPECT_EQ(TypeOf(vec4_f16)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
     EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
     EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
     EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+    EXPECT_EQ(TypeOf(vec4_f16), TypeOf(vec4_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromVec4) {
+    Enable(ast::Extension::kF16);
+
     auto* vec4_bool =
-        Construct(create<ast::Vector>(nullptr, 4), vec4<bool>(true, false, true, false));
-    auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), vec4<i32>(1_i, 2_i, 3_i, 4_i));
-    auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4), vec4<u32>(1_u, 2_u, 3_u, 4_u));
-    auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4), vec4<f32>(1_f, 2_f, 3_f, 4_f));
-    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+        Construct(create<ast::Vector>(nullptr, 4u), vec4<bool>(true, false, true, false));
+    auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4u), vec4<i32>(1_i, 2_i, 3_i, 4_i));
+    auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4u), vec4<u32>(1_u, 2_u, 3_u, 4_u));
+    auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4u), vec4<f32>(1_f, 2_f, 3_f, 4_f));
+    auto* vec4_f16 = Construct(create<ast::Vector>(nullptr, 4u), vec4<f16>(1_h, 2_h, 3_h, 4_h));
+    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32, vec4_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1842,30 +2081,38 @@
     ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec4_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec4_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+    EXPECT_EQ(TypeOf(vec4_f16)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
     EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
     EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
     EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+    EXPECT_EQ(TypeOf(vec4_f16), TypeOf(vec4_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromScalarAndVec3) {
+    Enable(ast::Extension::kF16);
+
     auto* vec4_bool =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(true), vec3<bool>(false, true, false));
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(true), vec3<bool>(false, true, false));
     auto* vec4_i32 =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(1_i), vec3<i32>(2_i, 3_i, 4_i));
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_i), vec3<i32>(2_i, 3_i, 4_i));
     auto* vec4_u32 =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(1_u), vec3<u32>(2_u, 3_u, 4_u));
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_u), vec3<u32>(2_u, 3_u, 4_u));
     auto* vec4_f32 =
-        Construct(create<ast::Vector>(nullptr, 4), Expr(1_f), vec3<f32>(2_f, 3_f, 4_f));
-    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_f), vec3<f32>(2_f, 3_f, 4_f));
+    auto* vec4_f16 =
+        Construct(create<ast::Vector>(nullptr, 4u), Expr(1_h), vec3<f16>(2_h, 3_h, 4_h));
+    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32, vec4_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1873,30 +2120,38 @@
     ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec4_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec4_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+    EXPECT_EQ(TypeOf(vec4_f16)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
     EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
     EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
     EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+    EXPECT_EQ(TypeOf(vec4_f16), TypeOf(vec4_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromVec2AndVec2) {
-    auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4), vec2<bool>(true, false),
+    Enable(ast::Extension::kF16);
+
+    auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4u), vec2<bool>(true, false),
                                 vec2<bool>(true, false));
     auto* vec4_i32 =
-        Construct(create<ast::Vector>(nullptr, 4), vec2<i32>(1_i, 2_i), vec2<i32>(3_i, 4_i));
+        Construct(create<ast::Vector>(nullptr, 4u), vec2<i32>(1_i, 2_i), vec2<i32>(3_i, 4_i));
     auto* vec4_u32 =
-        Construct(create<ast::Vector>(nullptr, 4), vec2<u32>(1_u, 2_u), vec2<u32>(3_u, 4_u));
+        Construct(create<ast::Vector>(nullptr, 4u), vec2<u32>(1_u, 2_u), vec2<u32>(3_u, 4_u));
     auto* vec4_f32 =
-        Construct(create<ast::Vector>(nullptr, 4), vec2<f32>(1_f, 2_f), vec2<f32>(3_f, 4_f));
-    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+        Construct(create<ast::Vector>(nullptr, 4u), vec2<f32>(1_f, 2_f), vec2<f32>(3_f, 4_f));
+    auto* vec4_f16 =
+        Construct(create<ast::Vector>(nullptr, 4u), vec2<f16>(1_h, 2_h), vec2<f16>(3_h, 4_h));
+    WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32, vec4_f16);
 
     ASSERT_TRUE(r()->Resolve()) << r()->error();
 
@@ -1904,29 +2159,33 @@
     ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
     ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(vec4_f16)->Is<sem::Vector>());
     EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
     EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
     EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
     EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(vec4_f16)->As<sem::Vector>()->type()->Is<sem::F16>());
     EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+    EXPECT_EQ(TypeOf(vec4_f16)->As<sem::Vector>()->Width(), 4u);
     EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
     EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
     EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
     EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+    EXPECT_EQ(TypeOf(vec4_f16), TypeOf(vec4_f16->target.type));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVectorElementTypeWithoutArgs) {
-    WrapInFunction(Construct(Source{{12, 34}}, create<ast::Vector>(nullptr, 3)));
+    WrapInFunction(Construct(Source{{12, 34}}, create<ast::Vector>(nullptr, 3u)));
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_THAT(r()->error(), HasSubstr("12:34 error: no matching constructor for vec3()"));
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec2ElementTypeFromScalarsMismatch) {
-    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 2),
+    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 2u),
                              Expr(Source{{1, 2}}, 1_i),  //
                              Expr(Source{{1, 3}}, 2_u)));
 
@@ -1935,7 +2194,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec3ElementTypeFromScalarsMismatch) {
-    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3),
+    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3u),
                              Expr(Source{{1, 2}}, 1_i),  //
                              Expr(Source{{1, 3}}, 2_u),  //
                              Expr(Source{{1, 4}}, 3_i)));
@@ -1946,7 +2205,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec3ElementTypeFromScalarAndVec2Mismatch) {
-    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3),
+    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3u),
                              Expr(Source{{1, 2}}, 1_i),  //
                              Construct(Source{{1, 3}}, ty.vec2<f32>(), 2_f, 3_f)));
 
@@ -1956,7 +2215,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromScalarsMismatch) {
-    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
+    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4u),
                              Expr(Source{{1, 2}}, 1_i),  //
                              Expr(Source{{1, 3}}, 2_i),  //
                              Expr(Source{{1, 4}}, 3_f),  //
@@ -1968,7 +2227,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromScalarAndVec3Mismatch) {
-    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
+    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4u),
                              Expr(Source{{1, 2}}, 1_i),  //
                              Construct(Source{{1, 3}}, ty.vec3<u32>(), 2_u, 3_u, 4_u)));
 
@@ -1978,7 +2237,7 @@
 }
 
 TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromVec2AndVec2Mismatch) {
-    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
+    WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4u),
                              Construct(Source{{1, 2}}, ty.vec2<i32>(), 3_i, 4_i),  //
                              Construct(Source{{1, 3}}, ty.vec2<u32>(), 3_u, 4_u)));
 
@@ -1990,35 +2249,60 @@
 }  // namespace VectorConstructor
 
 namespace MatrixConstructor {
-struct MatrixDimensions {
+
+struct MatrixParams {
+    using name_func_ptr = std::string (*)();
+
     uint32_t rows;
     uint32_t columns;
+    name_func_ptr get_element_type_name;
+    builder::ast_type_func_ptr create_element_ast_type;
+    builder::ast_expr_func_ptr create_element_ast_value;
+    builder::ast_type_func_ptr create_column_ast_type;
+    builder::ast_type_func_ptr create_mat_ast_type;
 };
 
-static std::string MatrixStr(const MatrixDimensions& dimensions) {
-    return "mat" + std::to_string(dimensions.columns) + "x" + std::to_string(dimensions.rows) +
-           "<f32>";
+template <typename T, uint32_t R, uint32_t C>
+constexpr MatrixParams MatrixParamsFor() {
+    return MatrixParams{
+        R,
+        C,
+        DataType<T>::Name,
+        DataType<T>::AST,
+        DataType<T>::Expr,
+        DataType<tint::resolver::builder::vec<R, T>>::AST,
+        DataType<tint::resolver::builder::mat<C, R, T>>::AST,
+    };
 }
 
-using MatrixConstructorTest = ResolverTestWithParam<MatrixDimensions>;
+static std::string MatrixStr(const MatrixParams& param) {
+    return "mat" + std::to_string(param.columns) + "x" + std::to_string(param.rows) + "<" +
+           param.get_element_type_name() + ">";
+}
+
+using MatrixConstructorTest = ResolverTestWithParam<MatrixParams>;
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooFewArguments) {
     // matNxM<f32>(vecM<f32>(), ...); with N - 1 arguments
+    // matNxM<f16>(vecM<f16>(), ...); with N - 1 arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
+    const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns - 1; i++) {
-        auto* vec_type = ty.vec<f32>(param.rows);
+        auto* vec_type = param.create_column_ast_type(*this);
         args.push_back(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
-        args_tys << "vec" << param.rows << "<f32>";
+        args_tys << "vec" << param.rows << "<" + element_type_name + ">";
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2029,20 +2313,24 @@
 
 TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_TooFewArguments) {
     // matNxM<f32>(f32,...,f32); with N*M - 1 arguments
+    // matNxM<f16>(f16,...,f16); with N*M - 1 arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
+    const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns * param.rows - 1; i++) {
-        args.push_back(Construct(ty.f32()));
+        args.push_back(Construct(param.create_element_ast_type(*this)));
         if (i > 0) {
             args_tys << ", ";
         }
-        args_tys << "f32";
+        args_tys << element_type_name;
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2053,21 +2341,25 @@
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooManyArguments) {
     // matNxM<f32>(vecM<f32>(), ...); with N + 1 arguments
+    // matNxM<f16>(vecM<f16>(), ...); with N + 1 arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
+    const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns + 1; i++) {
-        auto* vec_type = ty.vec<f32>(param.rows);
+        auto* vec_type = param.create_column_ast_type(*this);
         args.push_back(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
-        args_tys << "vec" << param.rows << "<f32>";
+        args_tys << "vec" << param.rows << "<" + element_type_name + ">";
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2078,20 +2370,24 @@
 
 TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_TooManyArguments) {
     // matNxM<f32>(f32,...,f32); with N*M + 1 arguments
+    // matNxM<f16>(f16,...,f16); with N*M + 1 arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
+    const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns * param.rows + 1; i++) {
-        args.push_back(Construct(ty.f32()));
+        args.push_back(Construct(param.create_element_ast_type(*this)));
         if (i > 0) {
             args_tys << ", ";
         }
-        args_tys << "f32";
+        args_tys << element_type_name;
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2102,9 +2398,12 @@
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_InvalidArgumentType) {
     // matNxM<f32>(vec<u32>, vec<u32>, ...); N arguments
+    // matNxM<f16>(vec<u32>, vec<u32>, ...); N arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
@@ -2116,7 +2415,7 @@
         args_tys << "vec" << param.rows << "<u32>";
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2127,9 +2426,12 @@
 
 TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_InvalidArgumentType) {
     // matNxM<f32>(u32, u32, ...); N*M arguments
+    // matNxM<f16>(u32, u32, ...); N*M arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
@@ -2140,7 +2442,7 @@
         args_tys << "u32";
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2151,6 +2453,7 @@
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooFewRowsInVectorArgument) {
     // matNxM<f32>(vecM<f32>(),...,vecM-1<f32>());
+    // matNxM<f16>(vecM<f16>(),...,vecM-1<f32>());
 
     const auto param = GetParam();
 
@@ -2159,22 +2462,25 @@
         return;
     }
 
+    Enable(ast::Extension::kF16);
+
+    const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        auto* valid_vec_type = ty.vec<f32>(param.rows);
+        auto* valid_vec_type = param.create_column_ast_type(*this);
         args.push_back(Construct(valid_vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
-        args_tys << "vec" << param.rows << "<f32>";
+        args_tys << "vec" << param.rows << "<" + element_type_name + ">";
     }
     const size_t kInvalidLoc = 2 * (param.columns - 1);
-    auto* invalid_vec_type = ty.vec<f32>(param.rows - 1);
+    auto* invalid_vec_type = ty.vec(param.create_element_ast_type(*this), param.rows - 1);
     args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
-    args_tys << ", vec" << (param.rows - 1) << "<f32>";
+    args_tys << ", vec" << (param.rows - 1) << "<" + element_type_name + ">";
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2185,6 +2491,7 @@
 
 TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooManyRowsInVectorArgument) {
     // matNxM<f32>(vecM<f32>(),...,vecM+1<f32>());
+    // matNxM<f16>(vecM<f16>(),...,vecM+1<f16>());
 
     const auto param = GetParam();
 
@@ -2193,21 +2500,24 @@
         return;
     }
 
+    Enable(ast::Extension::kF16);
+
+    const std::string element_type_name = param.get_element_type_name();
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        auto* valid_vec_type = ty.vec<f32>(param.rows);
+        auto* valid_vec_type = param.create_column_ast_type(*this);
         args.push_back(Construct(valid_vec_type));
         if (i > 0) {
             args_tys << ", ";
         }
-        args_tys << "vec" << param.rows << "<f32>";
+        args_tys << "vec" << param.rows << "<" + element_type_name + ">";
     }
-    auto* invalid_vec_type = ty.vec<f32>(param.rows + 1);
+    auto* invalid_vec_type = ty.vec(param.create_element_ast_type(*this), param.rows + 1);
     args.push_back(Construct(invalid_vec_type));
-    args_tys << ", vec" << (param.rows + 1) << "<f32>";
+    args_tys << ", vec" << (param.rows + 1) << "<" + element_type_name + ">";
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2218,9 +2528,13 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ZeroValue_Success) {
     // matNxM<f32>();
+    // matNxM<f16>();
 
     const auto param = GetParam();
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+
+    Enable(ast::Extension::kF16);
+
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{{12, 40}}, matrix_type);
     WrapInFunction(tc);
 
@@ -2229,16 +2543,19 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_WithColumns_Success) {
     // matNxM<f32>(vecM<f32>(), ...); with N arguments
+    // matNxM<f16>(vecM<f16>(), ...); with N arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        auto* vec_type = ty.vec<f32>(param.rows);
+        auto* vec_type = param.create_column_ast_type(*this);
         args.push_back(Construct(vec_type));
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2247,15 +2564,18 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_WithElements_Success) {
     // matNxM<f32>(f32,...,f32); with N*M arguments
+    // matNxM<f16>(f16,...,f16); with N*M arguments
 
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns * param.rows; i++) {
-        args.push_back(Construct(ty.f32()));
+        args.push_back(Construct(param.create_element_ast_type(*this)));
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2264,9 +2584,13 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Error) {
     // matNxM<Float32>(vecM<u32>(), ...); with N arguments
+    // matNxM<Float16>(vecM<u32>(), ...); with N arguments
 
     const auto param = GetParam();
-    auto* f32_alias = Alias("Float32", ty.f32());
+
+    Enable(ast::Extension::kF16);
+
+    auto* elem_type_alias = Alias("ElemType", param.create_element_ast_type(*this));
 
     std::stringstream args_tys;
     ast::ExpressionList args;
@@ -2279,7 +2603,7 @@
         args_tys << "vec" << param.rows << "<u32>";
     }
 
-    auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
+    auto* matrix_type = ty.mat(ty.Of(elem_type_alias), param.columns, param.rows);
     auto* tc = Construct(Source{{12, 34}}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2290,17 +2614,21 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Success) {
     // matNxM<Float32>(vecM<f32>(), ...); with N arguments
+    // matNxM<Float16>(vecM<f16>(), ...); with N arguments
 
     const auto param = GetParam();
-    auto* f32_alias = Alias("Float32", ty.f32());
+
+    Enable(ast::Extension::kF16);
+
+    auto* elem_type_alias = Alias("ElemType", param.create_element_ast_type(*this));
 
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        auto* vec_type = ty.vec<f32>(param.rows);
+        auto* vec_type = param.create_column_ast_type(*this);
         args.push_back(Construct(vec_type));
     }
 
-    auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
+    auto* matrix_type = ty.mat(ty.Of(elem_type_alias), param.columns, param.rows);
     auto* tc = Construct(Source{}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2320,9 +2648,12 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentTypeAlias_Success) {
     const auto param = GetParam();
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* vec_type = ty.vec<f32>(param.rows);
-    auto* vec_alias = Alias("VectorFloat2", vec_type);
+
+    Enable(ast::Extension::kF16);
+
+    auto* matrix_type = param.create_mat_ast_type(*this);
+    auto* vec_type = param.create_column_ast_type(*this);
+    auto* vec_alias = Alias("ColVectorAlias", vec_type);
 
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
@@ -2337,13 +2668,16 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentElementTypeAlias_Error) {
     const auto param = GetParam();
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
-    auto* f32_alias = Alias("UnsignedInt", ty.u32());
+
+    Enable(ast::Extension::kF16);
+
+    auto* matrix_type = param.create_mat_ast_type(*this);
+    auto* u32_type_alias = Alias("UnsignedInt", ty.u32());
 
     std::stringstream args_tys;
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
+        auto* vec_type = ty.vec(ty.Of(u32_type_alias), param.rows);
         args.push_back(Construct(vec_type));
         if (i > 0) {
             args_tys << ", ";
@@ -2361,15 +2695,18 @@
 
 TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentElementTypeAlias_Success) {
     const auto param = GetParam();
-    auto* f32_alias = Alias("Float32", ty.f32());
+
+    Enable(ast::Extension::kF16);
+
+    auto* elem_type_alias = Alias("ElemType", param.create_element_ast_type(*this));
 
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
+        auto* vec_type = ty.vec(ty.Of(elem_type_alias), param.rows);
         args.push_back(Construct(vec_type));
     }
 
-    auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+    auto* matrix_type = param.create_mat_ast_type(*this);
     auto* tc = Construct(Source{}, matrix_type, std::move(args));
     WrapInFunction(tc);
 
@@ -2379,9 +2716,11 @@
 TEST_P(MatrixConstructorTest, InferElementTypeFromVectors) {
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.columns; i++) {
-        args.push_back(Construct(ty.vec<f32>(param.rows)));
+        args.push_back(Construct(param.create_column_ast_type(*this)));
     }
 
     auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
@@ -2394,9 +2733,11 @@
 TEST_P(MatrixConstructorTest, InferElementTypeFromScalars) {
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     ast::ExpressionList args;
     for (uint32_t i = 0; i < param.rows * param.columns; i++) {
-        args.push_back(Expr(static_cast<f32>(i)));
+        args.push_back(param.create_element_ast_value(*this, static_cast<double>(i)));
     }
 
     auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
@@ -2408,6 +2749,8 @@
 TEST_P(MatrixConstructorTest, CannotInferElementTypeFromVectors_Mismatch) {
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     std::stringstream err;
     err << "12:34 error: no matching constructor for mat" << param.columns << "x" << param.rows
         << "(";
@@ -2422,8 +2765,8 @@
             args.push_back(Construct(ty.vec<i32>(param.rows)));
             err << "vec" << param.rows << "<i32>";
         } else {
-            args.push_back(Construct(ty.vec<f32>(param.rows)));
-            err << "vec" << param.rows << "<f32>";
+            args.push_back(Construct(param.create_column_ast_type(*this)));
+            err << "vec" << param.rows << "<" + param.get_element_type_name() + ">";
         }
     }
 
@@ -2437,6 +2780,8 @@
 TEST_P(MatrixConstructorTest, CannotInferElementTypeFromScalars_Mismatch) {
     const auto param = GetParam();
 
+    Enable(ast::Extension::kF16);
+
     std::stringstream err;
     err << "12:34 error: no matching constructor for mat" << param.columns << "x" << param.rows
         << "(";
@@ -2450,8 +2795,8 @@
             args.push_back(Expr(static_cast<i32>(i)));  // The odd one out
             err << "i32";
         } else {
-            args.push_back(Expr(static_cast<f32>(i)));
-            err << "f32";
+            args.push_back(param.create_element_ast_value(*this, static_cast<double>(i)));
+            err << param.get_element_type_name();
         }
     }
 
@@ -2466,15 +2811,24 @@
 
 INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
                          MatrixConstructorTest,
-                         testing::Values(MatrixDimensions{2, 2},
-                                         MatrixDimensions{3, 2},
-                                         MatrixDimensions{4, 2},
-                                         MatrixDimensions{2, 3},
-                                         MatrixDimensions{3, 3},
-                                         MatrixDimensions{4, 3},
-                                         MatrixDimensions{2, 4},
-                                         MatrixDimensions{3, 4},
-                                         MatrixDimensions{4, 4}));
+                         testing::Values(MatrixParamsFor<f32, 2, 2>(),
+                                         MatrixParamsFor<f32, 3, 2>(),
+                                         MatrixParamsFor<f32, 4, 2>(),
+                                         MatrixParamsFor<f32, 2, 3>(),
+                                         MatrixParamsFor<f32, 3, 3>(),
+                                         MatrixParamsFor<f32, 4, 3>(),
+                                         MatrixParamsFor<f32, 2, 4>(),
+                                         MatrixParamsFor<f32, 3, 4>(),
+                                         MatrixParamsFor<f32, 4, 4>(),
+                                         MatrixParamsFor<f16, 2, 2>(),
+                                         MatrixParamsFor<f16, 3, 2>(),
+                                         MatrixParamsFor<f16, 4, 2>(),
+                                         MatrixParamsFor<f16, 2, 3>(),
+                                         MatrixParamsFor<f16, 3, 3>(),
+                                         MatrixParamsFor<f16, 4, 3>(),
+                                         MatrixParamsFor<f16, 2, 4>(),
+                                         MatrixParamsFor<f16, 3, 4>(),
+                                         MatrixParamsFor<f16, 4, 4>()));
 }  // namespace MatrixConstructor
 
 namespace StructConstructor {
@@ -2492,13 +2846,18 @@
     CreatePtrsFor<u32>(),          //
     CreatePtrsFor<i32>(),          //
     CreatePtrsFor<f32>(),          //
+    CreatePtrsFor<f16>(),          //
     CreatePtrsFor<vec4<bool>>(),   //
     CreatePtrsFor<vec2<i32>>(),    //
     CreatePtrsFor<vec3<u32>>(),    //
     CreatePtrsFor<vec4<f32>>(),    //
+    CreatePtrsFor<vec4<f16>>(),    //
     CreatePtrsFor<mat2x2<f32>>(),  //
     CreatePtrsFor<mat3x3<f32>>(),  //
-    CreatePtrsFor<mat4x4<f32>>()   //
+    CreatePtrsFor<mat4x4<f32>>(),  //
+    CreatePtrsFor<mat2x2<f16>>(),  //
+    CreatePtrsFor<mat3x3<f16>>(),  //
+    CreatePtrsFor<mat4x4<f16>>()   //
 };
 
 auto number_of_members = testing::Values(2u, 32u, 64u);
@@ -2511,6 +2870,8 @@
     auto& str_params = std::get<0>(param);
     uint32_t N = std::get<1>(param);
 
+    Enable(ast::Extension::kF16);
+
     ast::StructMemberList members;
     ast::ExpressionList values;
     for (uint32_t i = 0; i < N; i++) {
@@ -2534,6 +2895,8 @@
     auto& str_params = std::get<0>(param);
     uint32_t N = std::get<1>(param);
 
+    Enable(ast::Extension::kF16);
+
     ast::StructMemberList members;
     ast::ExpressionList values;
     for (uint32_t i = 0; i < N + 1; i++) {
@@ -2565,6 +2928,8 @@
     auto& ctor_params = std::get<1>(param);
     uint32_t N = std::get<2>(param);
 
+    Enable(ast::Extension::kF16);
+
     if (str_params.ast == ctor_params.ast) {
         return;
     }
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index ec2f8f5..e746e51 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -82,14 +82,14 @@
 
 TEST_F(ResolverTypeValidationTest, GlobalVariableWithStorageClass_Pass) {
     // var<private> global_var: f32;
-    Global(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
-    // let global_var: f32;
-    GlobalConst(Source{{12, 34}}, "global_var", ty.f32(), Construct(ty.f32()));
+    // const global_const: f32 = f32();
+    GlobalConst(Source{{12, 34}}, "global_const", ty.f32(), Construct(ty.f32()));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -98,9 +98,9 @@
     // var global_var0 : f32 = 0.1;
     // var global_var1 : i32 = 0;
 
-    Global("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1_f));
+    GlobalVar("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1_f));
 
-    Global(Source{{12, 34}}, "global_var1", ty.f32(), ast::StorageClass::kPrivate, Expr(1_f));
+    GlobalVar(Source{{12, 34}}, "global_var1", ty.f32(), ast::StorageClass::kPrivate, Expr(1_f));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -116,7 +116,7 @@
              Decl(Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2_f))),
          });
 
-    Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -180,162 +180,181 @@
 
 TEST_F(ResolverTypeValidationTest, ArraySize_AIntLiteral_Pass) {
     // var<private> a : array<f32, 4>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_a)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_a)), ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLiteral_Pass) {
     // var<private> a : array<f32, 4u>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_u)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_u)), ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Pass) {
     // var<private> a : array<f32, 4i>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_i)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4_i)), ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLet_Pass) {
-    // let size = 4u;
+TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedConst_Pass) {
+    // const size = 4u;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(4_u));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_SignedLet_Pass) {
-    // let size = 4i;
+TEST_F(ResolverTypeValidationTest, ArraySize_SignedConst_Pass) {
+    // const size = 4i;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(4_i));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_AIntLiteral_Zero) {
     // var<private> a : array<f32, 0>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_a)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_a)), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLiteral_Zero) {
     // var<private> a : array<f32, 0u>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_u)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_u)), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Zero) {
     // var<private> a : array<f32, 0i>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_i)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0_i)), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Negative) {
     // var<private> a : array<f32, -10i>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, -10_i)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, -10_i)), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (-10) must be greater than 0");
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLet_Zero) {
-    // let size = 0u;
+TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedConst_Zero) {
+    // const size = 0u;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(0_u));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_SignedLet_Zero) {
-    // let size = 0i;
+TEST_F(ResolverTypeValidationTest, ArraySize_SignedConst_Zero) {
+    // const size = 0i;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(0_i));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (0) must be greater than 0");
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_SignedLet_Negative) {
-    // let size = -10i;
+TEST_F(ResolverTypeValidationTest, ArraySize_SignedConst_Negative) {
+    // const size = -10i;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(-10_i));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+    EXPECT_EQ(r()->error(), "12:34 error: array size (-10) must be greater than 0");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_FloatLiteral) {
     // var<private> a : array<f32, 10.0>;
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 10_f)), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 10_f)), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+    EXPECT_EQ(r()->error(),
+              "12:34 error: array size must evaluate to a constant integer expression, but is type "
+              "'f32'");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_IVecLiteral) {
     // var<private> a : array<f32, vec2<i32>(10, 10)>;
-    Global("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.vec2<i32>(), 10_i, 10_i)),
-           ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.vec2<i32>(), 10_i, 10_i)),
+              ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+    EXPECT_EQ(r()->error(),
+              "12:34 error: array size must evaluate to a constant integer expression, but is type "
+              "'vec2<i32>'");
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_FloatLet) {
-    // let size = 10.0;
+TEST_F(ResolverTypeValidationTest, ArraySize_FloatConst) {
+    // const size = 10.0;
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Expr(10_f));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+    EXPECT_EQ(r()->error(),
+              "12:34 error: array size must evaluate to a constant integer expression, but is type "
+              "'f32'");
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_IVecLet) {
-    // let size = vec2<i32>(100, 100);
+TEST_F(ResolverTypeValidationTest, ArraySize_IVecConst) {
+    // const size = vec2<i32>(100, 100);
     // var<private> a : array<f32, size>;
     GlobalConst("size", nullptr, Construct(ty.vec2<i32>(), 100_i, 100_i));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+    EXPECT_EQ(r()->error(),
+              "12:34 error: array size must evaluate to a constant integer expression, but is type "
+              "'vec2<i32>'");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_TooBig_ImplicitStride) {
     // var<private> a : array<f32, 0x40000000u>;
-    Global("a", ty.array(Source{{12, 34}}, ty.f32(), 0x40000000_u), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(Source{{12, 34}}, ty.f32(), 0x40000000_u), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size in bytes must not exceed 0xffffffff, but "
-              "is 0x100000000");
+              "12:34 error: array size (0x100000000) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_TooBig_ExplicitStride) {
     // var<private> a : @stride(8) array<f32, 0x20000000u>;
-    Global("a", ty.array(Source{{12, 34}}, ty.f32(), 0x20000000_u, 8), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(Source{{12, 34}}, ty.f32(), 0x20000000_u, 8),
+              ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size in bytes must not exceed 0xffffffff, but "
-              "is 0x100000000");
+              "12:34 error: array size (0x100000000) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_Overridable) {
     // override size = 10i;
     // var<private> a : array<f32, size>;
     Override("size", nullptr, Expr(10_i));
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size identifier must be a literal or a module-scope 'let'");
+              "12:34 error: array size must evaluate to a constant integer expression");
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_ModuleVar) {
     // var<private> size : i32 = 10i;
     // var<private> a : array<f32, size>;
-    Global("size", ty.i32(), Expr(10_i), ast::StorageClass::kPrivate);
-    Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+    GlobalVar("size", ty.i32(), Expr(10_i), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size identifier must be a literal or a module-scope 'let'");
+              R"(12:34 error: var 'size' cannot not be referenced at module-scope
+note: var 'size' declared here)");
+}
+
+TEST_F(ResolverTypeValidationTest, ArraySize_FunctionConst) {
+    // {
+    //   const size = 10;
+    //   var a : array<f32, size>;
+    // }
+    auto* size = Const("size", nullptr, Expr(10_i));
+    auto* a = Var("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")));
+    WrapInFunction(size, a);
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, ArraySize_FunctionLet) {
@@ -345,19 +364,17 @@
     // }
     auto* size = Let("size", nullptr, Expr(10_i));
     auto* a = Var("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")));
-    WrapInFunction(Block(Decl(size), Decl(a)));
+    WrapInFunction(size, a);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: array size identifier must be a literal or a module-scope 'let'");
+              "12:34 error: array size must evaluate to a constant integer expression");
 }
 
-TEST_F(ResolverTypeValidationTest, ArraySize_InvalidExpr) {
+TEST_F(ResolverTypeValidationTest, ArraySize_ComplexExpr) {
     // var a : array<f32, i32(4i)>;
     auto* a = Var("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.i32(), 4_i)));
-    WrapInFunction(Block(Decl(a)));
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: array size identifier must be a literal or a module-scope 'let'");
+    WrapInFunction(a);
+    EXPECT_TRUE(r()->Resolve());
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayInFunction_Fail) {
@@ -377,7 +394,7 @@
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
               R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
-12:34 note: while instantiating variable a)");
+12:34 note: while instantiating 'var' a)");
 }
 
 TEST_F(ResolverTypeValidationTest, Struct_Member_VectorNoType) {
@@ -385,7 +402,7 @@
     //   a: vec3;
     // };
 
-    Structure("S", {Member("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3))});
+    Structure("S", {Member("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
@@ -395,7 +412,7 @@
     // struct S {
     //   a: mat3x3;
     // };
-    Structure("S", {Member("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3))});
+    Structure("S", {Member("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u))});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
@@ -417,8 +434,7 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: struct size in bytes must not exceed 0xffffffff, but "
-              "is 0x100000000");
+              "12:34 error: struct size (0x100000000) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, Struct_MemberOffset_TooBig) {
@@ -438,8 +454,7 @@
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: struct member has byte offset 0x100000000, but must "
-              "not exceed 0xffffffff");
+              "12:34 error: struct member offset (0x100000000) must not exceed 0xffffffff bytes");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLast_Pass) {
@@ -467,8 +482,7 @@
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
-              "12:34 error: an array element type cannot contain a runtime-sized "
-              "array");
+              "12:34 error: an array element type cannot contain a runtime-sized array");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayInStructInArray) {
@@ -478,12 +492,11 @@
     // var<private> a : array<Foo, 4>;
 
     auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
-    Global("v", ty.array(Source{{12, 34}}, ty.Of(foo), 4_u), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.array(Source{{12, 34}}, ty.Of(foo), 4_u), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
-              "12:34 error: an array element type cannot contain a runtime-sized "
-              "array");
+              "12:34 error: an array element type cannot contain a runtime-sized array");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayInStructInStruct) {
@@ -499,8 +512,8 @@
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
-              "12:34 error: a struct that contains a runtime array cannot be "
-              "nested inside another struct");
+              "12:34 error: a struct that contains a runtime array cannot be nested inside another "
+              "struct");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayIsNotLast_Fail) {
@@ -522,13 +535,13 @@
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayAsGlobalVariable) {
-    Global(Source{{56, 78}}, "g", ty.array<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{56, 78}}, "g", ty.array<i32>(), ast::StorageClass::kPrivate);
 
     ASSERT_FALSE(r()->Resolve());
 
     EXPECT_EQ(r()->error(),
               R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayAsLocalVariable) {
@@ -539,7 +552,7 @@
 
     EXPECT_EQ(r()->error(),
               R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
-56:78 note: while instantiating variable g)");
+56:78 note: while instantiating 'var' g)");
 }
 
 TEST_F(ResolverTypeValidationTest, RuntimeArrayAsParameter_Fail) {
@@ -601,8 +614,7 @@
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
     EXPECT_EQ(r()->error(),
-              "12:34 error: runtime arrays may only appear as the last member of "
-              "a struct");
+              "12:34 error: runtime arrays may only appear as the last member of a struct");
 }
 
 TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsLast_Pass) {
@@ -625,19 +637,18 @@
 
 TEST_F(ResolverTypeValidationTest, ArrayOfNonStorableType) {
     auto* tex_ty = ty.sampled_texture(ast::TextureDimension::k2d, ty.f32());
-    Global("arr", ty.array(Source{{12, 34}}, tex_ty, 4_i), ast::StorageClass::kPrivate);
+    GlobalVar("arr", ty.array(Source{{12, 34}}, tex_ty, 4_i), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: texture_2d<f32> cannot be used as an element type of "
-              "an array");
+              "12:34 error: texture_2d<f32> cannot be used as an element type of an array");
 }
 
 TEST_F(ResolverTypeValidationTest, VariableAsType) {
     // var<private> a : i32;
     // var<private> b : a;
-    Global("a", ty.i32(), ast::StorageClass::kPrivate);
-    Global("b", ty.type_name("a"), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.type_name("a"), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -649,7 +660,7 @@
     // fn f() {}
     // var<private> v : f;
     Func("f", {}, ty.void_(), {});
-    Global("v", ty.type_name("f"), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.type_name("f"), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
@@ -659,7 +670,7 @@
 
 TEST_F(ResolverTypeValidationTest, BuiltinAsType) {
     // var<private> v : max;
-    Global("v", ty.type_name("max"), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.type_name("max"), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
@@ -670,14 +681,14 @@
     // var<private> v : f16;
     Enable(ast::Extension::kF16);
 
-    Global("v", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.f16(), ast::StorageClass::kPrivate);
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
 TEST_F(ResolverTypeValidationTest, F16TypeUsedWithoutExtension) {
     // var<private> v : f16;
-    Global("v", ty.f16(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.f16(), ast::StorageClass::kPrivate);
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "error: f16 used without 'f16' extension enabled");
@@ -749,8 +760,8 @@
 using SampledTextureDimensionTest = ResolverTestWithParam<DimensionParams>;
 TEST_P(SampledTextureDimensionTest, All) {
     auto& params = GetParam();
-    Global(Source{{12, 34}}, "a", ty.sampled_texture(params.dim, ty.i32()),
-           ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar(Source{{12, 34}}, "a", ty.sampled_texture(params.dim, ty.i32()),
+              ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -767,8 +778,8 @@
 using MultisampledTextureDimensionTest = ResolverTestWithParam<DimensionParams>;
 TEST_P(MultisampledTextureDimensionTest, All) {
     auto& params = GetParam();
-    Global("a", ty.multisampled_texture(Source{{12, 34}}, params.dim, ty.i32()),
-           ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", ty.multisampled_texture(Source{{12, 34}}, params.dim, ty.i32()),
+              ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -818,7 +829,7 @@
 using SampledTextureTypeTest = ResolverTestWithParam<TypeParams>;
 TEST_P(SampledTextureTypeTest, All) {
     auto& params = GetParam();
-    Global(
+    GlobalVar(
         "a",
         ty.sampled_texture(Source{{12, 34}}, ast::TextureDimension::k2d, params.type_func(*this)),
         ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
@@ -837,10 +848,10 @@
 using MultisampledTextureTypeTest = ResolverTestWithParam<TypeParams>;
 TEST_P(MultisampledTextureTypeTest, All) {
     auto& params = GetParam();
-    Global("a",
-           ty.multisampled_texture(Source{{12, 34}}, ast::TextureDimension::k2d,
-                                   params.type_func(*this)),
-           ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a",
+              ty.multisampled_texture(Source{{12, 34}}, ast::TextureDimension::k2d,
+                                      params.type_func(*this)),
+              ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -879,7 +890,7 @@
     auto* st = ty.storage_texture(Source{{12, 34}}, params.dim, ast::TexelFormat::kR32Uint,
                                   ast::Access::kWrite);
 
-    Global("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -929,17 +940,17 @@
 
     auto* st_a = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d, params.format,
                                     ast::Access::kWrite);
-    Global("a", st_a, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st_a, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
 
     auto* st_b = ty.storage_texture(ast::TextureDimension::k2d, params.format, ast::Access::kWrite);
-    Global("b", st_b, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 1)});
+    GlobalVar("b", st_b, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 1)});
 
     auto* st_c =
         ty.storage_texture(ast::TextureDimension::k2dArray, params.format, ast::Access::kWrite);
-    Global("c", st_c, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 2)});
+    GlobalVar("c", st_c, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 2)});
 
     auto* st_d = ty.storage_texture(ast::TextureDimension::k3d, params.format, ast::Access::kWrite);
-    Global("d", st_d, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 3)});
+    GlobalVar("d", st_d, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 3)});
 
     if (params.is_valid) {
         EXPECT_TRUE(r()->Resolve()) << r()->error();
@@ -963,7 +974,7 @@
     auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
                                   ast::TexelFormat::kR32Uint, ast::Access::kUndefined);
 
-    Global("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(), "12:34 error: storage texture missing access control");
@@ -976,12 +987,12 @@
     auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
                                   ast::TexelFormat::kR32Uint, ast::Access::kReadWrite);
 
-    Global("a", st, ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone, nullptr,
+              ast::AttributeList{GroupAndBinding(0, 0)});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: storage textures currently only support 'write' "
-              "access control");
+              "12:34 error: storage textures currently only support 'write' access control");
 }
 
 TEST_F(StorageTextureAccessTest, ReadOnlyAccess_Fail) {
@@ -991,12 +1002,12 @@
     auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
                                   ast::TexelFormat::kR32Uint, ast::Access::kRead);
 
-    Global("a", st, ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone, nullptr,
+              ast::AttributeList{GroupAndBinding(0, 0)});
 
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: storage textures currently only support 'write' "
-              "access control");
+              "12:34 error: storage textures currently only support 'write' access control");
 }
 
 TEST_F(StorageTextureAccessTest, WriteOnlyAccess_Pass) {
@@ -1006,7 +1017,8 @@
     auto* st = ty.storage_texture(ast::TextureDimension::k1d, ast::TexelFormat::kR32Uint,
                                   ast::Access::kWrite);
 
-    Global("a", st, ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
+    GlobalVar("a", st, ast::StorageClass::kNone, nullptr,
+              ast::AttributeList{GroupAndBinding(0, 0)});
 
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
@@ -1029,8 +1041,11 @@
 TEST_P(ValidMatrixTypes, Okay) {
     // var a : matNxM<EL_TY>;
     auto& params = GetParam();
-    Global("a", ty.mat(params.elem_ty(*this), params.columns, params.rows),
-           ast::StorageClass::kPrivate);
+
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("a", ty.mat(params.elem_ty(*this), params.columns, params.rows),
+              ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
@@ -1046,16 +1061,31 @@
                                          ParamsFor<f32>(4, 4),
                                          ParamsFor<alias<f32>>(4, 2),
                                          ParamsFor<alias<f32>>(4, 3),
-                                         ParamsFor<alias<f32>>(4, 4)));
+                                         ParamsFor<alias<f32>>(4, 4),
+                                         ParamsFor<f16>(2, 2),
+                                         ParamsFor<f16>(2, 3),
+                                         ParamsFor<f16>(2, 4),
+                                         ParamsFor<f16>(3, 2),
+                                         ParamsFor<f16>(3, 3),
+                                         ParamsFor<f16>(3, 4),
+                                         ParamsFor<f16>(4, 2),
+                                         ParamsFor<f16>(4, 3),
+                                         ParamsFor<f16>(4, 4),
+                                         ParamsFor<alias<f16>>(4, 2),
+                                         ParamsFor<alias<f16>>(4, 3),
+                                         ParamsFor<alias<f16>>(4, 4)));
 
 using InvalidMatrixElementTypes = ResolverTestWithParam<Params>;
 TEST_P(InvalidMatrixElementTypes, InvalidElementType) {
     // var a : matNxM<EL_TY>;
     auto& params = GetParam();
-    Global("a", ty.mat(Source{{12, 34}}, params.elem_ty(*this), params.columns, params.rows),
-           ast::StorageClass::kPrivate);
+
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("a", ty.mat(Source{{12, 34}}, params.elem_ty(*this), params.columns, params.rows),
+              ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: matrix element type must be 'f32'");
+    EXPECT_EQ(r()->error(), "12:34 error: matrix element type must be 'f32' or 'f16'");
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
                          InvalidMatrixElementTypes,
@@ -1063,12 +1093,17 @@
                                          ParamsFor<i32>(4, 3),
                                          ParamsFor<u32>(4, 4),
                                          ParamsFor<vec2<f32>>(2, 2),
+                                         ParamsFor<vec2<f16>>(2, 2),
                                          ParamsFor<vec3<i32>>(2, 3),
                                          ParamsFor<vec4<u32>>(2, 4),
                                          ParamsFor<mat2x2<f32>>(3, 2),
                                          ParamsFor<mat3x3<f32>>(3, 3),
                                          ParamsFor<mat4x4<f32>>(3, 4),
-                                         ParamsFor<array<2, f32>>(4, 2)));
+                                         ParamsFor<mat2x2<f16>>(3, 2),
+                                         ParamsFor<mat3x3<f16>>(3, 3),
+                                         ParamsFor<mat4x4<f16>>(3, 4),
+                                         ParamsFor<array<2, f32>>(4, 2),
+                                         ParamsFor<array<2, f16>>(4, 2)));
 }  // namespace MatrixTests
 
 namespace VectorTests {
@@ -1086,25 +1121,32 @@
 TEST_P(ValidVectorTypes, Okay) {
     // var a : vecN<EL_TY>;
     auto& params = GetParam();
-    Global("a", ty.vec(params.elem_ty(*this), params.width), ast::StorageClass::kPrivate);
+
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("a", ty.vec(params.elem_ty(*this), params.width), ast::StorageClass::kPrivate);
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
                          ValidVectorTypes,
                          testing::Values(ParamsFor<bool>(2),
                                          ParamsFor<f32>(2),
+                                         ParamsFor<f16>(2),
                                          ParamsFor<i32>(2),
                                          ParamsFor<u32>(2),
                                          ParamsFor<bool>(3),
                                          ParamsFor<f32>(3),
+                                         ParamsFor<f16>(3),
                                          ParamsFor<i32>(3),
                                          ParamsFor<u32>(3),
                                          ParamsFor<bool>(4),
                                          ParamsFor<f32>(4),
+                                         ParamsFor<f16>(4),
                                          ParamsFor<i32>(4),
                                          ParamsFor<u32>(4),
                                          ParamsFor<alias<bool>>(4),
                                          ParamsFor<alias<f32>>(4),
+                                         ParamsFor<alias<f16>>(4),
                                          ParamsFor<alias<i32>>(4),
                                          ParamsFor<alias<u32>>(4)));
 
@@ -1112,11 +1154,14 @@
 TEST_P(InvalidVectorElementTypes, InvalidElementType) {
     // var a : vecN<EL_TY>;
     auto& params = GetParam();
-    Global("a", ty.vec(Source{{12, 34}}, params.elem_ty(*this), params.width),
-           ast::StorageClass::kPrivate);
+
+    Enable(ast::Extension::kF16);
+
+    GlobalVar("a", ty.vec(Source{{12, 34}}, params.elem_ty(*this), params.width),
+              ast::StorageClass::kPrivate);
     EXPECT_FALSE(r()->Resolve());
     EXPECT_EQ(r()->error(),
-              "12:34 error: vector element type must be 'bool', 'f32', 'i32' "
+              "12:34 error: vector element type must be 'bool', 'f32', 'f16', 'i32' "
               "or 'u32'");
 }
 INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
@@ -1125,7 +1170,7 @@
                                          ParamsFor<vec3<i32>>(2),
                                          ParamsFor<vec4<u32>>(2),
                                          ParamsFor<mat2x2<f32>>(2),
-                                         ParamsFor<mat3x3<f32>>(2),
+                                         ParamsFor<mat3x3<f16>>(2),
                                          ParamsFor<mat4x4<f32>>(2),
                                          ParamsFor<array<2, f32>>(2)));
 }  // namespace VectorTests
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 15ca72f..57bf6b0 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -982,7 +982,7 @@
         };
 
         auto name = builder_->Symbols().NameFor(ident->symbol);
-        auto* sem = sem_.Get<sem::VariableUser>(ident)->Variable();
+        auto* sem = sem_.Get(ident)->UnwrapMaterialize()->As<sem::VariableUser>()->Variable();
         auto* node = CreateNode(name + "_ident_expr", ident);
         return Switch(
             sem,
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index 002f841..b335c27 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -84,7 +84,7 @@
         kTrue,
         kFalse,
         kLiteral,
-        kModuleLet,
+        kModuleConst,
         kPipelineOverridable,
         kFuncLetUniformRhs,
         kFuncVarUniform,
@@ -137,8 +137,8 @@
                 return "false";
             case kLiteral:
                 return "7 == 7";
-            case kModuleLet:
-                return "module_let == 0";
+            case kModuleConst:
+                return "module_const == 0";
             case kPipelineOverridable:
                 return "pipeline_overridable == 0";
             case kFuncLetUniformRhs:
@@ -231,7 +231,7 @@
             CASE(kTrue);
             CASE(kFalse);
             CASE(kLiteral);
-            CASE(kModuleLet);
+            CASE(kModuleConst);
             CASE(kPipelineOverridable);
             CASE(kFuncLetUniformRhs);
             CASE(kFuncVarUniform);
@@ -290,7 +290,7 @@
 @group(1) @binding(2) var s : sampler;
 @group(1) @binding(3) var sc : sampler_comparison;
 
-let module_let : i32 = 42;
+const module_const : i32 = 42;
 @id(42) override pipeline_overridable : i32;
 
 fn user_no_restriction() {}
@@ -5327,7 +5327,7 @@
     //     workgroupBarrier();
     //   }
     // }
-    b.Global("non_uniform_global", ty.i32(), ast::StorageClass::kPrivate);
+    b.GlobalVar("non_uniform_global", ty.i32(), ast::StorageClass::kPrivate);
     ast::StatementList main_body;
     ast::ExpressionList args;
     for (int i = 0; i < 255; i++) {
@@ -6538,7 +6538,7 @@
     //     workgroupBarrier();
     //   }
     // }
-    b.Global("v0", ty.i32(), ast::StorageClass::kPrivate, b.Expr(0_i));
+    b.GlobalVar("v0", ty.i32(), ast::StorageClass::kPrivate, b.Expr(0_i));
     ast::StatementList foo_body;
     std::string v_last = "v0";
     for (int i = 1; i < 100000; i++) {
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 9177d6b..20cf1ea 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -61,8 +61,8 @@
 };
 
 TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInVertexStage) {
-    Global(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
-    Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
     Func(Source{{9, 10}}, "f0", {}, ty.vec4<f32>(),
@@ -93,8 +93,8 @@
     //  f1();
     //}
 
-    Global(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
-    Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
 
     Func(Source{{5, 6}}, "f2", {}, ty.void_(), {stmt});
@@ -221,7 +221,7 @@
     //   return;
     // }
 
-    Global("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+    GlobalVar("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
 
     Func("my_func", {}, ty.void_(),
          {
@@ -324,11 +324,11 @@
 
 TEST_F(ResolverValidationTest, StorageClass_SamplerExplicitStorageClass) {
     auto* t = ty.sampler(ast::SamplerKind::kSampler);
-    Global(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     EXPECT_FALSE(r()->Resolve());
 
@@ -338,11 +338,11 @@
 
 TEST_F(ResolverValidationTest, StorageClass_TextureExplicitStorageClass) {
     auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
-    Global(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     EXPECT_FALSE(r()->Resolve()) << r()->error();
 
@@ -351,7 +351,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
-    Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "xyqz");
 
@@ -363,7 +363,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
-    Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
 
     auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "rgyw");
 
@@ -376,7 +376,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
-    Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* ident = Expr(Source{{{3, 3}, {3, 8}}}, "zzzzz");
     auto* mem = MemberAccessor("my_vec", ident);
@@ -387,7 +387,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadIndex) {
-    Global("my_vec", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec2<f32>(), ast::StorageClass::kPrivate);
 
     auto* ident = Expr(Source{{3, 3}}, "z");
     auto* mem = MemberAccessor("my_vec", ident);
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index e03aa13..a2e7799 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -307,31 +307,31 @@
     return true;
 }
 
-bool Validator::Materialize(const sem::Materialize* m) const {
-    auto* from = m->Expr()->Type();
-    auto* to = m->Type();
-
+bool Validator::Materialize(const sem::Type* to,
+                            const sem::Type* from,
+                            const Source& source) const {
     if (sem::Type::ConversionRank(from, to) == sem::Type::kNoConversion) {
         AddError("cannot convert value of type '" + sem_.TypeNameOf(from) + "' to type '" +
                      sem_.TypeNameOf(to) + "'",
-                 m->Expr()->Declaration()->source);
+                 source);
         return false;
     }
     return true;
 }
 
-bool Validator::VariableConstructorOrCast(const ast::Variable* v,
-                                          ast::StorageClass storage_class,
-                                          const sem::Type* storage_ty,
-                                          const sem::Type* rhs_ty) const {
-    auto* value_type = rhs_ty->UnwrapRef();  // Implicit load of RHS
+bool Validator::VariableInitializer(const ast::Variable* v,
+                                    ast::StorageClass storage_class,
+                                    const sem::Type* storage_ty,
+                                    const sem::Expression* initializer) const {
+    auto* initializer_ty = initializer->Type();
+    auto* value_type = initializer_ty->UnwrapRef();  // Implicit load of RHS
 
     // Value type has to match storage type
     if (storage_ty != value_type) {
-        std::string decl = v->Is<ast::Let>() ? "let" : "var";
-        AddError("cannot initialize " + decl + " of type '" + sem_.TypeNameOf(storage_ty) +
-                     "' with value of type '" + sem_.TypeNameOf(rhs_ty) + "'",
-                 v->source);
+        std::stringstream s;
+        s << "cannot initialize " << v->Kind() << " of type '" << sem_.TypeNameOf(storage_ty)
+          << "' with value of type '" << sem_.TypeNameOf(initializer_ty) << "'";
+        AddError(s.str(), v->source);
         return false;
     }
 
@@ -529,12 +529,52 @@
     std::unordered_map<uint32_t, const sem::Variable*> constant_ids,
     std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const {
     auto* decl = global->Declaration();
-    if (!NoDuplicateAttributes(decl->attributes)) {
-        return false;
-    }
-
     bool ok = Switch(
         decl,  //
+        [&](const ast::Var* var) {
+            if (global->StorageClass() == ast::StorageClass::kNone) {
+                AddError("module-scope 'var' declaration must have a storage class", decl->source);
+                return false;
+            }
+
+            for (auto* attr : decl->attributes) {
+                bool is_shader_io_attribute =
+                    attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
+                                  ast::InvariantAttribute, ast::LocationAttribute>();
+                bool has_io_storage_class = global->StorageClass() == ast::StorageClass::kInput ||
+                                            global->StorageClass() == ast::StorageClass::kOutput;
+                if (!attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
+                                   ast::InternalAttribute>() &&
+                    (!is_shader_io_attribute || !has_io_storage_class)) {
+                    AddError("attribute is not valid for module-scope 'var'", attr->source);
+                    return false;
+                }
+            }
+
+            // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
+            // The access mode always has a default, and except for variables in the
+            // storage storage class, must not be written.
+            if (global->StorageClass() != ast::StorageClass::kStorage &&
+                var->declared_access != ast::Access::kUndefined) {
+                AddError("only variables in <storage> storage class may declare an access mode",
+                         var->source);
+                return false;
+            }
+
+            if (!AtomicVariable(global, atomic_composite_info)) {
+                return false;
+            }
+
+            return Var(global);
+        },
+        [&](const ast::Let*) {
+            if (!decl->attributes.empty()) {
+                AddError("attribute is not valid for module-scope 'let' declaration",
+                         decl->attributes[0]->source);
+                return false;
+            }
+            return Let(global);
+        },
         [&](const ast::Override*) {
             for (auto* attr : decl->attributes) {
                 if (auto* id_attr = attr->As<ast::IdAttribute>()) {
@@ -558,31 +598,21 @@
                     return false;
                 }
             }
-            return true;
+            return Override(global);
         },
-        [&](const ast::Let*) {
+        [&](const ast::Const*) {
             if (!decl->attributes.empty()) {
-                AddError("attribute is not valid for module-scope 'let' declaration",
+                AddError("attribute is not valid for module-scope 'const' declaration",
                          decl->attributes[0]->source);
                 return false;
             }
-            return true;
+            return Const(global);
         },
-        [&](const ast::Var*) {
-            for (auto* attr : decl->attributes) {
-                bool is_shader_io_attribute =
-                    attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
-                                  ast::InvariantAttribute, ast::LocationAttribute>();
-                bool has_io_storage_class = global->StorageClass() == ast::StorageClass::kInput ||
-                                            global->StorageClass() == ast::StorageClass::kOutput;
-                if (!attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
-                                   ast::InternalAttribute>() &&
-                    (!is_shader_io_attribute || !has_io_storage_class)) {
-                    AddError("attribute is not valid for module-scope 'var'", attr->source);
-                    return false;
-                }
-            }
-            return true;
+        [&](Default) {
+            TINT_ICE(Resolver, diagnostics_)
+                << "Validator::GlobalVariable() called with a unknown variable type: "
+                << decl->TypeInfo().name;
+            return false;
         });
 
     if (!ok) {
@@ -618,23 +648,7 @@
             }
     }
 
-    if (auto* var = decl->As<ast::Var>()) {
-        // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
-        // The access mode always has a default, and except for variables in the
-        // storage storage class, must not be written.
-        if (global->StorageClass() != ast::StorageClass::kStorage &&
-            var->declared_access != ast::Access::kUndefined) {
-            AddError("only variables in <storage> storage class may declare an access mode",
-                     var->source);
-            return false;
-        }
-
-        if (!AtomicVariable(global, atomic_composite_info)) {
-            return false;
-        }
-    }
-
-    return Variable(global);
+    return true;
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
@@ -680,65 +694,119 @@
 
 bool Validator::Variable(const sem::Variable* v) const {
     auto* decl = v->Declaration();
+    return Switch(
+        decl,                                               //
+        [&](const ast::Var*) { return Var(v); },            //
+        [&](const ast::Let*) { return Let(v); },            //
+        [&](const ast::Override*) { return Override(v); },  //
+        [&](const ast::Const*) { return true; },            //
+        [&](Default) {
+            TINT_ICE(Resolver, diagnostics_)
+                << "Validator::Variable() called with a unknown variable type: "
+                << decl->TypeInfo().name;
+            return false;
+        });
+}
+
+bool Validator::Var(const sem::Variable* v) const {
+    auto* var = v->Declaration()->As<ast::Var>();
     auto* storage_ty = v->Type()->UnwrapRef();
 
-    auto* as_let = decl->As<ast::Let>();
-    auto* as_var = decl->As<ast::Var>();
-
     if (v->Is<sem::GlobalVariable>()) {
-        auto name = symbols_.NameFor(decl->symbol);
+        auto name = symbols_.NameFor(var->symbol);
         if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
-            auto* kind = as_let ? "let" : "var";
-            AddError(
-                "'" + name + "' is a builtin and cannot be redeclared as a module-scope " + kind,
-                decl->source);
+            AddError("'" + name + "' is a builtin and cannot be redeclared as a module-scope 'var'",
+                     var->source);
             return false;
         }
     }
 
-    if (as_var && !IsStorable(storage_ty)) {
-        AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a var",
-                 decl->source);
+    if (!IsStorable(storage_ty)) {
+        AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a var", var->source);
         return false;
     }
 
-    if (as_let && !(storage_ty->IsConstructible() || storage_ty->Is<sem::Pointer>())) {
-        AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a let",
-                 decl->source);
-        return false;
-    }
-
-    if (v->Is<sem::LocalVariable>() && as_var &&
-        IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreStorageClass)) {
+    if (v->Is<sem::LocalVariable>() &&
+        IsValidationEnabled(var->attributes, ast::DisabledValidation::kIgnoreStorageClass)) {
         if (!v->Type()->UnwrapRef()->IsConstructible()) {
-            AddError("function variable must have a constructible type",
-                     decl->type ? decl->type->source : decl->source);
+            AddError("function-scope 'var' must have a constructible type",
+                     var->type ? var->type->source : var->source);
             return false;
         }
     }
 
-    if (as_var && storage_ty->is_handle() &&
-        as_var->declared_storage_class != ast::StorageClass::kNone) {
+    if (storage_ty->is_handle() && var->declared_storage_class != ast::StorageClass::kNone) {
         // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
         // If the store type is a texture type or a sampler type, then the
         // variable declaration must not have a storage class attribute. The
         // storage class will always be handle.
         AddError(
             "variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a storage class",
-            decl->source);
+            var->source);
         return false;
     }
 
-    if (IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreStorageClass) &&
-        as_var &&
-        (as_var->declared_storage_class == ast::StorageClass::kInput ||
-         as_var->declared_storage_class == ast::StorageClass::kOutput)) {
-        AddError("invalid use of input/output storage class", as_var->source);
+    if (IsValidationEnabled(var->attributes, ast::DisabledValidation::kIgnoreStorageClass) &&
+        (var->declared_storage_class == ast::StorageClass::kInput ||
+         var->declared_storage_class == ast::StorageClass::kOutput)) {
+        AddError("invalid use of input/output storage class", var->source);
         return false;
     }
     return true;
 }
 
+bool Validator::Let(const sem::Variable* v) const {
+    auto* decl = v->Declaration();
+    auto* storage_ty = v->Type()->UnwrapRef();
+
+    if (v->Is<sem::GlobalVariable>()) {
+        auto name = symbols_.NameFor(decl->symbol);
+        if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+            AddError("'" + name + "' is a builtin and cannot be redeclared as a 'let'",
+                     decl->source);
+            return false;
+        }
+    }
+
+    if (!(storage_ty->IsConstructible() || storage_ty->Is<sem::Pointer>())) {
+        AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a 'let'",
+                 decl->source);
+        return false;
+    }
+    return true;
+}
+
+bool Validator::Override(const sem::Variable* v) const {
+    auto* decl = v->Declaration();
+    auto* storage_ty = v->Type()->UnwrapRef();
+
+    auto name = symbols_.NameFor(decl->symbol);
+    if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+        AddError("'" + name + "' is a builtin and cannot be redeclared as a 'override'",
+                 decl->source);
+        return false;
+    }
+
+    if (!storage_ty->is_scalar()) {
+        AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a 'override'",
+                 decl->source);
+        return false;
+    }
+    return true;
+}
+
+bool Validator::Const(const sem::Variable* v) const {
+    auto* decl = v->Declaration();
+
+    auto name = symbols_.NameFor(decl->symbol);
+    if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+        AddError("'" + name + "' is a builtin and cannot be redeclared as a 'const'", decl->source);
+        return false;
+    }
+
+    return true;
+}
+
 bool Validator::Parameter(const ast::Function* func, const sem::Variable* var) const {
     auto* decl = var->Declaration();
 
@@ -1536,16 +1604,16 @@
     auto& signature = builtin->Signature();
 
     auto check_arg_is_constexpr = [&](sem::ParameterUsage usage, int min, int max) {
-        auto index = signature.IndexOf(usage);
-        if (index < 0) {
+        auto signed_index = signature.IndexOf(usage);
+        if (signed_index < 0) {
             return true;
         }
+        auto index = static_cast<size_t>(signed_index);
         std::string name = sem::str(usage);
         auto* arg = call->Arguments()[index];
         if (auto values = arg->ConstantValue()) {
             // Assert that the constant values are of the expected type.
-            if (!values.Type()->IsAnyOf<sem::I32, sem::Vector>() ||
-                !values.ElementType()->Is<sem::I32>()) {
+            if (!values->Type()->is_integer_scalar_or_vector()) {
                 TINT_ICE(Resolver, diagnostics_)
                     << "failed to resolve '" + func_name + "' " << name << " parameter type";
                 return false;
@@ -1563,24 +1631,26 @@
                     return ast::TraverseAction::Stop;
                 });
             if (is_const_expr) {
-                auto vector = builtin->Parameters()[index]->Type()->Is<sem::Vector>();
-                for (size_t i = 0, n = values.ElementCount(); i < n; i++) {
-                    auto value = values.Element<AInt>(i).value;
-                    if (value < min || value > max) {
-                        if (vector) {
+                if (auto* vector = builtin->Parameters()[index]->Type()->As<sem::Vector>()) {
+                    for (size_t i = 0; i < vector->Width(); i++) {
+                        auto value = values->Index(i)->As<AInt>();
+                        if (value < min || value > max) {
                             AddError("each component of the " + name +
                                          " argument must be at least " + std::to_string(min) +
                                          " and at most " + std::to_string(max) + ". " + name +
                                          " component " + std::to_string(i) + " is " +
                                          std::to_string(value),
                                      arg->Declaration()->source);
-                        } else {
-                            AddError("the " + name + " argument must be at least " +
-                                         std::to_string(min) + " and at most " +
-                                         std::to_string(max) + ". " + name + " is " +
-                                         std::to_string(value),
-                                     arg->Declaration()->source);
+                            return false;
                         }
+                    }
+                } else {
+                    auto value = values->As<AInt>();
+                    if (value < min || value > max) {
+                        AddError("the " + name + " argument must be at least " +
+                                     std::to_string(min) + " and at most " + std::to_string(max) +
+                                     ". " + name + " is " + std::to_string(value),
+                                 arg->Declaration()->source);
                         return false;
                     }
                 }
@@ -1625,6 +1695,11 @@
     auto sym = decl->target.name->symbol;
     auto name = symbols_.NameFor(sym);
 
+    if (!current_statement) {  // Function call at module-scope.
+        AddError("functions cannot be called at module-scope", decl->source);
+        return false;
+    }
+
     if (target->Declaration()->IsEntryPoint()) {
         // https://www.w3.org/TR/WGSL/#function-restriction
         // An entry point must never be the target of a function call.
@@ -1797,7 +1872,7 @@
 
 bool Validator::Vector(const sem::Vector* ty, const Source& source) const {
     if (!ty->type()->is_scalar()) {
-        AddError("vector element type must be 'bool', 'f32', 'i32' or 'u32'", source);
+        AddError("vector element type must be 'bool', 'f32', 'f16', 'i32' or 'u32'", source);
         return false;
     }
     return true;
@@ -1805,7 +1880,7 @@
 
 bool Validator::Matrix(const sem::Matrix* ty, const Source& source) const {
     if (!ty->is_float_matrix()) {
-        AddError("matrix element type must be 'f32'", source);
+        AddError("matrix element type must be 'f32' or 'f16'", source);
         return false;
     }
     return true;
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 30dcaf9..4fdac53 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -283,10 +283,12 @@
     /// @returns true on success, false otherwise.
     bool LoopStatement(const sem::LoopStatement* stmt) const;
 
-    /// Validates a materialize of an abstract numeric value
-    /// @param m the materialize to validate
+    /// Validates a materialize of an abstract numeric value from the type `from` to the type `to`.
+    /// @param to the target type
+    /// @param from the abstract numeric type
+    /// @param source the source of the materialization
     /// @returns true on success, false otherwise
-    bool Materialize(const sem::Materialize* m) const;
+    bool Materialize(const sem::Type* to, const sem::Type* from, const Source& source) const;
 
     /// Validates a matrix
     /// @param ty the matrix to validate
@@ -352,20 +354,40 @@
     bool SwitchStatement(const ast::SwitchStatement* s);
 
     /// Validates a variable
-    /// @param var the variable to validate
+    /// @param v the variable to validate
     /// @returns true on success, false otherwise.
-    bool Variable(const sem::Variable* var) const;
+    bool Variable(const sem::Variable* v) const;
 
-    /// Validates a variable constructor or cast
+    /// Validates a 'var' variable declaration
+    /// @param v the variable to validate
+    /// @returns true on success, false otherwise.
+    bool Var(const sem::Variable* v) const;
+
+    /// Validates a 'let' variable declaration
+    /// @param v the variable to validate
+    /// @returns true on success, false otherwise.
+    bool Let(const sem::Variable* v) const;
+
+    /// Validates a 'override' variable declaration
+    /// @param v the variable to validate
+    /// @returns true on success, false otherwise.
+    bool Override(const sem::Variable* v) const;
+
+    /// Validates a 'const' variable declaration
+    /// @param v the variable to validate
+    /// @returns true on success, false otherwise.
+    bool Const(const sem::Variable* v) const;
+
+    /// Validates a variable initializer
     /// @param v the variable to validate
     /// @param storage_class the storage class of the variable
     /// @param storage_type the type of the storage
-    /// @param rhs_type the right hand side of the expression
+    /// @param initializer the RHS initializer expression
     /// @returns true on succes, false otherwise
-    bool VariableConstructorOrCast(const ast::Variable* v,
-                                   ast::StorageClass storage_class,
-                                   const sem::Type* storage_type,
-                                   const sem::Type* rhs_type) const;
+    bool VariableInitializer(const ast::Variable* v,
+                             ast::StorageClass storage_class,
+                             const sem::Type* storage_type,
+                             const sem::Expression* initializer) const;
 
     /// Validates a vector
     /// @param ty the vector to validate
diff --git a/src/tint/resolver/validator_is_storeable_test.cc b/src/tint/resolver/validator_is_storeable_test.cc
index a5f612c..88ec911 100644
--- a/src/tint/resolver/validator_is_storeable_test.cc
+++ b/src/tint/resolver/validator_is_storeable_test.cc
@@ -32,6 +32,7 @@
     EXPECT_TRUE(v()->IsStorable(create<sem::I32>()));
     EXPECT_TRUE(v()->IsStorable(create<sem::U32>()));
     EXPECT_TRUE(v()->IsStorable(create<sem::F32>()));
+    EXPECT_TRUE(v()->IsStorable(create<sem::F16>()));
 }
 
 TEST_F(ValidatorIsStorableTest, Vector) {
@@ -44,21 +45,36 @@
     EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 2u)));
     EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 3u)));
     EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 4u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F16>(), 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F16>(), 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F16>(), 4u)));
 }
 
 TEST_F(ValidatorIsStorableTest, Matrix) {
-    auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
-    auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
-    auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 2u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 3u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 4u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 2u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 3u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 4u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 2u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 3u)));
-    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 4u)));
+    auto* vec2_f32 = create<sem::Vector>(create<sem::F32>(), 2u);
+    auto* vec3_f32 = create<sem::Vector>(create<sem::F32>(), 3u);
+    auto* vec4_f32 = create<sem::Vector>(create<sem::F32>(), 4u);
+    auto* vec2_f16 = create<sem::Vector>(create<sem::F16>(), 2u);
+    auto* vec3_f16 = create<sem::Vector>(create<sem::F16>(), 3u);
+    auto* vec4_f16 = create<sem::Vector>(create<sem::F16>(), 4u);
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2_f32, 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2_f32, 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2_f32, 4u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3_f32, 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3_f32, 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3_f32, 4u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4_f32, 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4_f32, 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4_f32, 4u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2_f16, 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2_f16, 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2_f16, 4u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3_f16, 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3_f16, 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3_f16, 4u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4_f16, 2u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4_f16, 3u)));
+    EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4_f16, 4u)));
 }
 
 TEST_F(ValidatorIsStorableTest, Pointer) {
diff --git a/src/tint/resolver/var_let_test.cc b/src/tint/resolver/var_let_test.cc
deleted file mode 100644
index 4306736..0000000
--- a/src/tint/resolver/var_let_test.cc
+++ /dev/null
@@ -1,676 +0,0 @@
-// Copyright 2021 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/resolver/resolver.h"
-#include "src/tint/resolver/resolver_test_helper.h"
-#include "src/tint/sem/reference.h"
-
-#include "gmock/gmock.h"
-
-using namespace tint::number_suffixes;  // NOLINT
-
-namespace tint::resolver {
-namespace {
-
-struct ResolverVarLetTest : public resolver::TestHelper, public testing::Test {};
-
-TEST_F(ResolverVarLetTest, VarDeclWithoutConstructor) {
-    // struct S { i : i32; }
-    // alias A = S;
-    // fn F(){
-    //   var i : i32;
-    //   var u : u32;
-    //   var f : f32;
-    //   var b : bool;
-    //   var s : S;
-    //   var a : A;
-    // }
-
-    auto* S = Structure("S", {Member("i", ty.i32())});
-    auto* A = Alias("A", ty.Of(S));
-
-    auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
-    auto* u = Var("u", ty.u32(), ast::StorageClass::kNone);
-    auto* f = Var("f", ty.f32(), ast::StorageClass::kNone);
-    auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone);
-    auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone);
-    auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone);
-
-    Func("F", {}, ty.void_(),
-         {
-             Decl(i),
-             Decl(u),
-             Decl(f),
-             Decl(b),
-             Decl(s),
-             Decl(a),
-         });
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    // `var` declarations are always of reference type
-    ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
-
-    EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
-    EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
-    EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
-    EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
-    EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
-    EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
-
-    EXPECT_EQ(Sem().Get(i)->Constructor(), nullptr);
-    EXPECT_EQ(Sem().Get(u)->Constructor(), nullptr);
-    EXPECT_EQ(Sem().Get(f)->Constructor(), nullptr);
-    EXPECT_EQ(Sem().Get(b)->Constructor(), nullptr);
-    EXPECT_EQ(Sem().Get(s)->Constructor(), nullptr);
-    EXPECT_EQ(Sem().Get(a)->Constructor(), nullptr);
-}
-
-TEST_F(ResolverVarLetTest, VarDeclWithConstructor) {
-    // struct S { i : i32; }
-    // alias A = S;
-    // fn F(){
-    //   var i : i32 = 1i;
-    //   var u : u32 = 1u;
-    //   var f : f32 = 1.f;
-    //   var b : bool = true;
-    //   var s : S = S(1);
-    //   var a : A = A(1);
-    // }
-
-    auto* S = Structure("S", {Member("i", ty.i32())});
-    auto* A = Alias("A", ty.Of(S));
-
-    auto* i_c = Expr(1_i);
-    auto* u_c = Expr(1_u);
-    auto* f_c = Expr(1_f);
-    auto* b_c = Expr(true);
-    auto* s_c = Construct(ty.Of(S), Expr(1_i));
-    auto* a_c = Construct(ty.Of(A), Expr(1_i));
-
-    auto* i = Var("i", ty.i32(), ast::StorageClass::kNone, i_c);
-    auto* u = Var("u", ty.u32(), ast::StorageClass::kNone, u_c);
-    auto* f = Var("f", ty.f32(), ast::StorageClass::kNone, f_c);
-    auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone, b_c);
-    auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone, s_c);
-    auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone, a_c);
-
-    Func("F", {}, ty.void_(),
-         {
-             Decl(i),
-             Decl(u),
-             Decl(f),
-             Decl(b),
-             Decl(s),
-             Decl(a),
-         });
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    // `var` declarations are always of reference type
-    ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
-
-    EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
-    EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
-    EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
-    EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
-    EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
-    EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
-
-    EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
-    EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
-    EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
-    EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
-    EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
-    EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
-}
-
-TEST_F(ResolverVarLetTest, LetDecl) {
-    // struct S { i : i32; }
-    // fn F(){
-    //   var v : i32;
-    //   let i : i32 = 1i;
-    //   let u : u32 = 1u;
-    //   let f : f32 = 1.;
-    //   let b : bool = true;
-    //   let s : S = S(1);
-    //   let a : A = A(1);
-    //   let p : pointer<function, i32> = &v;
-    // }
-
-    auto* S = Structure("S", {Member("i", ty.i32())});
-    auto* A = Alias("A", ty.Of(S));
-    auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
-
-    auto* i_c = Expr(1_i);
-    auto* u_c = Expr(1_u);
-    auto* f_c = Expr(1_f);
-    auto* b_c = Expr(true);
-    auto* s_c = Construct(ty.Of(S), Expr(1_i));
-    auto* a_c = Construct(ty.Of(A), Expr(1_i));
-    auto* p_c = AddressOf(v);
-
-    auto* i = Let("i", ty.i32(), i_c);
-    auto* u = Let("u", ty.u32(), u_c);
-    auto* f = Let("f", ty.f32(), f_c);
-    auto* b = Let("b", ty.bool_(), b_c);
-    auto* s = Let("s", ty.Of(S), s_c);
-    auto* a = Let("a", ty.Of(A), a_c);
-    auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
-
-    Func("F", {}, ty.void_(),
-         {
-             Decl(v),
-             Decl(i),
-             Decl(u),
-             Decl(f),
-             Decl(b),
-             Decl(s),
-             Decl(a),
-             Decl(p),
-         });
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    // `let` declarations are always of the storage type
-    ASSERT_TRUE(TypeOf(i)->Is<sem::I32>());
-    ASSERT_TRUE(TypeOf(u)->Is<sem::U32>());
-    ASSERT_TRUE(TypeOf(f)->Is<sem::F32>());
-    ASSERT_TRUE(TypeOf(b)->Is<sem::Bool>());
-    ASSERT_TRUE(TypeOf(s)->Is<sem::Struct>());
-    ASSERT_TRUE(TypeOf(a)->Is<sem::Struct>());
-    ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
-    ASSERT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
-
-    EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
-    EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
-    EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
-    EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
-    EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
-    EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
-    EXPECT_EQ(Sem().Get(p)->Constructor()->Declaration(), p_c);
-}
-
-TEST_F(ResolverVarLetTest, DefaultVarStorageClass) {
-    // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
-
-    auto* buf = Structure("S", {Member("m", ty.i32())});
-    auto* function = Var("f", ty.i32());
-    auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
-    auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
-    auto* uniform = Global("ub", ty.Of(buf), ast::StorageClass::kUniform,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(0),
-                               create<ast::GroupAttribute>(0),
-                           });
-    auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(1),
-                               create<ast::GroupAttribute>(0),
-                           });
-    auto* handle = Global("h", ty.depth_texture(ast::TextureDimension::k2d),
-                          ast::AttributeList{
-                              create<ast::BindingAttribute>(2),
-                              create<ast::GroupAttribute>(0),
-                          });
-
-    WrapInFunction(function);
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    ASSERT_TRUE(TypeOf(function)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(workgroup)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(uniform)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(handle)->Is<sem::Reference>());
-
-    EXPECT_EQ(TypeOf(function)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
-    EXPECT_EQ(TypeOf(private_)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
-    EXPECT_EQ(TypeOf(workgroup)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
-    EXPECT_EQ(TypeOf(uniform)->As<sem::Reference>()->Access(), ast::Access::kRead);
-    EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), ast::Access::kRead);
-    EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
-}
-
-TEST_F(ResolverVarLetTest, ExplicitVarStorageClass) {
-    // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
-
-    auto* buf = Structure("S", {Member("m", ty.i32())});
-    auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(1),
-                               create<ast::GroupAttribute>(0),
-                           });
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
-
-    EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
-}
-
-TEST_F(ResolverVarLetTest, LetInheritsAccessFromOriginatingVariable) {
-    // struct Inner {
-    //    arr: array<i32, 4>;
-    // }
-    // struct S {
-    //    inner: Inner;
-    // }
-    // @group(0) @binding(0) var<storage, read_write> s : S;
-    // fn f() {
-    //   let p = &s.inner.arr[4];
-    // }
-    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
-    auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(0),
-                               create<ast::GroupAttribute>(0),
-                           });
-
-    auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4_i);
-    auto* ptr = Let("p", nullptr, AddressOf(expr));
-
-    WrapInFunction(ptr);
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
-    ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
-
-    EXPECT_EQ(TypeOf(expr)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
-    EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsAlias) {
-    // type a = i32;
-    //
-    // fn X() {
-    //   var a = false;
-    // }
-    //
-    // fn Y() {
-    //   let a = true;
-    // }
-
-    auto* t = Alias("a", ty.i32());
-    auto* v = Var("a", nullptr, Expr(false));
-    auto* l = Let("a", nullptr, Expr(false));
-    Func("X", {}, ty.void_(), {Decl(v)});
-    Func("Y", {}, ty.void_(), {Decl(l)});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* type_t = Sem().Get(t);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), type_t);
-    EXPECT_EQ(local_l->Shadows(), type_t);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsStruct) {
-    // struct a {
-    //   m : i32;
-    // };
-    //
-    // fn X() {
-    //   var a = true;
-    // }
-    //
-    // fn Y() {
-    //   let a = false;
-    // }
-
-    auto* t = Structure("a", {Member("m", ty.i32())});
-    auto* v = Var("a", nullptr, Expr(false));
-    auto* l = Let("a", nullptr, Expr(false));
-    Func("X", {}, ty.void_(), {Decl(v)});
-    Func("Y", {}, ty.void_(), {Decl(l)});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* type_t = Sem().Get(t);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), type_t);
-    EXPECT_EQ(local_l->Shadows(), type_t);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsFunction) {
-    // fn a() {
-    //   var a = true;
-    // }
-    //
-    // fn b() {
-    //   let b = false;
-    // }
-
-    auto* v = Var("a", nullptr, Expr(false));
-    auto* l = Let("b", nullptr, Expr(false));
-    auto* fa = Func("a", {}, ty.void_(), {Decl(v)});
-    auto* fb = Func("b", {}, ty.void_(), {Decl(l)});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-    auto* func_a = Sem().Get(fa);
-    auto* func_b = Sem().Get(fb);
-
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-    ASSERT_NE(func_a, nullptr);
-    ASSERT_NE(func_b, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), func_a);
-    EXPECT_EQ(local_l->Shadows(), func_b);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsGlobalVar) {
-    // var<private> a : i32;
-    //
-    // fn X() {
-    //   var a = a;
-    // }
-    //
-    // fn Y() {
-    //   let a = a;
-    // }
-
-    auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
-    auto* v = Var("a", nullptr, Expr("a"));
-    auto* l = Let("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(v)});
-    Func("Y", {}, ty.void_(), {Decl(l)});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* global = Sem().Get(g);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), global);
-    EXPECT_EQ(local_l->Shadows(), global);
-
-    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
-    auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
-
-    ASSERT_NE(user_v, nullptr);
-    ASSERT_NE(user_l, nullptr);
-
-    EXPECT_EQ(user_v->Variable(), global);
-    EXPECT_EQ(user_l->Variable(), global);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsGlobalLet) {
-    // let a : i32 = 1;
-    //
-    // fn X() {
-    //   var a = (a == 123);
-    // }
-    //
-    // fn Y() {
-    //   let a = (a == 321);
-    // }
-
-    auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
-    auto* v = Var("a", nullptr, Expr("a"));
-    auto* l = Let("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(v)});
-    Func("Y", {}, ty.void_(), {Decl(l)});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* global = Sem().Get(g);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), global);
-    EXPECT_EQ(local_l->Shadows(), global);
-
-    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
-    auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
-
-    ASSERT_NE(user_v, nullptr);
-    ASSERT_NE(user_l, nullptr);
-
-    EXPECT_EQ(user_v->Variable(), global);
-    EXPECT_EQ(user_l->Variable(), global);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsLocalVar) {
-    // fn X() {
-    //   var a : i32;
-    //   {
-    //     var a = a;
-    //   }
-    //   {
-    //     let a = a;
-    //   }
-    // }
-
-    auto* s = Var("a", ty.i32(), Expr(1_i));
-    auto* v = Var("a", nullptr, Expr("a"));
-    auto* l = Let("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* local_s = Sem().Get<sem::LocalVariable>(s);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(local_s, nullptr);
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), local_s);
-    EXPECT_EQ(local_l->Shadows(), local_s);
-
-    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
-    auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
-
-    ASSERT_NE(user_v, nullptr);
-    ASSERT_NE(user_l, nullptr);
-
-    EXPECT_EQ(user_v->Variable(), local_s);
-    EXPECT_EQ(user_l->Variable(), local_s);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsLocalLet) {
-    // fn X() {
-    //   let a = 1;
-    //   {
-    //     var a = (a == 123);
-    //   }
-    //   {
-    //     let a = (a == 321);
-    //   }
-    // }
-
-    auto* s = Let("a", ty.i32(), Expr(1_i));
-    auto* v = Var("a", nullptr, Expr("a"));
-    auto* l = Let("a", nullptr, Expr("a"));
-    Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* local_s = Sem().Get<sem::LocalVariable>(s);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(local_s, nullptr);
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), local_s);
-    EXPECT_EQ(local_l->Shadows(), local_s);
-
-    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
-    auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
-
-    ASSERT_NE(user_v, nullptr);
-    ASSERT_NE(user_l, nullptr);
-
-    EXPECT_EQ(user_v->Variable(), local_s);
-    EXPECT_EQ(user_l->Variable(), local_s);
-}
-
-TEST_F(ResolverVarLetTest, LocalShadowsParam) {
-    // fn F(a : i32) {
-    //   {
-    //     var a = a;
-    //   }
-    //   {
-    //     let a = a;
-    //   }
-    // }
-
-    auto* p = Param("a", ty.i32());
-    auto* v = Var("a", nullptr, Expr("a"));
-    auto* l = Let("a", nullptr, Expr("a"));
-    Func("X", {p}, ty.void_(), {Block(Decl(v)), Block(Decl(l))});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* param = Sem().Get<sem::Parameter>(p);
-    auto* local_v = Sem().Get<sem::LocalVariable>(v);
-    auto* local_l = Sem().Get<sem::LocalVariable>(l);
-
-    ASSERT_NE(param, nullptr);
-    ASSERT_NE(local_v, nullptr);
-    ASSERT_NE(local_l, nullptr);
-
-    EXPECT_EQ(local_v->Shadows(), param);
-    EXPECT_EQ(local_l->Shadows(), param);
-
-    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
-    auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
-
-    ASSERT_NE(user_v, nullptr);
-    ASSERT_NE(user_l, nullptr);
-
-    EXPECT_EQ(user_v->Variable(), param);
-    EXPECT_EQ(user_l->Variable(), param);
-}
-
-TEST_F(ResolverVarLetTest, ParamShadowsFunction) {
-    // fn a(a : bool) {
-    // }
-
-    auto* p = Param("a", ty.bool_());
-    auto* f = Func("a", {p}, ty.void_(), {});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* func = Sem().Get(f);
-    auto* param = Sem().Get<sem::Parameter>(p);
-
-    ASSERT_NE(func, nullptr);
-    ASSERT_NE(param, nullptr);
-
-    EXPECT_EQ(param->Shadows(), func);
-}
-
-TEST_F(ResolverVarLetTest, ParamShadowsGlobalVar) {
-    // var<private> a : i32;
-    //
-    // fn F(a : bool) {
-    // }
-
-    auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
-    auto* p = Param("a", ty.bool_());
-    Func("F", {p}, ty.void_(), {});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* global = Sem().Get(g);
-    auto* param = Sem().Get<sem::Parameter>(p);
-
-    ASSERT_NE(global, nullptr);
-    ASSERT_NE(param, nullptr);
-
-    EXPECT_EQ(param->Shadows(), global);
-}
-
-TEST_F(ResolverVarLetTest, ParamShadowsGlobalLet) {
-    // let a : i32 = 1;
-    //
-    // fn F(a : bool) {
-    // }
-
-    auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
-    auto* p = Param("a", ty.bool_());
-    Func("F", {p}, ty.void_(), {});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* global = Sem().Get(g);
-    auto* param = Sem().Get<sem::Parameter>(p);
-
-    ASSERT_NE(global, nullptr);
-    ASSERT_NE(param, nullptr);
-
-    EXPECT_EQ(param->Shadows(), global);
-}
-
-TEST_F(ResolverVarLetTest, ParamShadowsAlias) {
-    // type a = i32;
-    //
-    // fn F(a : a) {
-    // }
-
-    auto* a = Alias("a", ty.i32());
-    auto* p = Param("a", ty.type_name("a"));
-    Func("F", {p}, ty.void_(), {});
-
-    ASSERT_TRUE(r()->Resolve()) << r()->error();
-
-    auto* alias = Sem().Get(a);
-    auto* param = Sem().Get<sem::Parameter>(p);
-
-    ASSERT_NE(alias, nullptr);
-    ASSERT_NE(param, nullptr);
-
-    EXPECT_EQ(param->Shadows(), alias);
-    EXPECT_EQ(param->Type(), alias);
-}
-
-}  // namespace
-}  // namespace tint::resolver
diff --git a/src/tint/resolver/var_let_validation_test.cc b/src/tint/resolver/var_let_validation_test.cc
deleted file mode 100644
index 67ecd53..0000000
--- a/src/tint/resolver/var_let_validation_test.cc
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright 2021 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/resolver/resolver.h"
-#include "src/tint/resolver/resolver_test_helper.h"
-
-#include "gmock/gmock.h"
-
-using namespace tint::number_suffixes;  // NOLINT
-
-namespace tint::resolver {
-namespace {
-
-struct ResolverVarLetValidationTest : public resolver::TestHelper, public testing::Test {};
-
-TEST_F(ResolverVarLetValidationTest, VarNoInitializerNoType) {
-    // var a;
-    WrapInFunction(Var(Source{{12, 34}}, "a", nullptr));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: function-scope 'var' declaration requires a type or initializer");
-}
-
-TEST_F(ResolverVarLetValidationTest, GlobalVarNoInitializerNoType) {
-    // var a;
-    Global(Source{{12, 34}}, "a", nullptr);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: module-scope 'var' declaration requires a type or initializer");
-}
-
-TEST_F(ResolverVarLetValidationTest, VarTypeNotStorable) {
-    // var i : i32;
-    // var p : pointer<function, i32> = &v;
-    auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
-    auto* p = Var(Source{{56, 78}}, "a", ty.pointer<i32>(ast::StorageClass::kFunction),
-                  ast::StorageClass::kNone, AddressOf(Source{{12, 34}}, "i"));
-    WrapInFunction(i, p);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "56:78 error: ptr<function, i32, read_write> cannot be used as the "
-              "type of a var");
-}
-
-TEST_F(ResolverVarLetValidationTest, LetTypeNotConstructible) {
-    // @group(0) @binding(0) var t1 : texture_2d<f32>;
-    // let t2 : t1;
-    auto* t1 = Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
-                      GroupAndBinding(0, 0));
-    auto* t2 = Let(Source{{56, 78}}, "t2", nullptr, Expr(t1));
-    WrapInFunction(t2);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "56:78 error: texture_2d<f32> cannot be used as the type of a let");
-}
-
-TEST_F(ResolverVarLetValidationTest, LetConstructorWrongType) {
-    // var v : i32 = 2u
-    WrapInFunction(Let(Source{{3, 3}}, "v", ty.i32(), Expr(2_u)));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
-}
-
-TEST_F(ResolverVarLetValidationTest, VarConstructorWrongType) {
-    // var v : i32 = 2u
-    WrapInFunction(Var(Source{{3, 3}}, "v", ty.i32(), ast::StorageClass::kNone, Expr(2_u)));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
-}
-
-TEST_F(ResolverVarLetValidationTest, LetConstructorWrongTypeViaAlias) {
-    auto* a = Alias("I32", ty.i32());
-    WrapInFunction(Let(Source{{3, 3}}, "v", ty.Of(a), Expr(2_u)));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
-}
-
-TEST_F(ResolverVarLetValidationTest, VarConstructorWrongTypeViaAlias) {
-    auto* a = Alias("I32", ty.i32());
-    WrapInFunction(Var(Source{{3, 3}}, "v", ty.Of(a), ast::StorageClass::kNone, Expr(2_u)));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
-}
-
-TEST_F(ResolverVarLetValidationTest, LetOfPtrConstructedWithRef) {
-    // var a : f32;
-    // let b : ptr<function,f32> = a;
-    const auto priv = ast::StorageClass::kFunction;
-    auto* var_a = Var("a", ty.f32(), priv);
-    auto* var_b = Let(Source{{12, 34}}, "b", ty.pointer<f32>(priv), Expr("a"), {});
-    WrapInFunction(var_a, var_b);
-
-    ASSERT_FALSE(r()->Resolve());
-
-    EXPECT_EQ(
-        r()->error(),
-        R"(12:34 error: cannot initialize let of type 'ptr<function, f32, read_write>' with value of type 'f32')");
-}
-
-TEST_F(ResolverVarLetValidationTest, LocalLetRedeclared) {
-    // let l : f32 = 1.;
-    // let l : i32 = 0;
-    auto* l1 = Let("l", ty.f32(), Expr(1_f));
-    auto* l2 = Let(Source{{12, 34}}, "l", ty.i32(), Expr(0_i));
-    WrapInFunction(l1, l2);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: redeclaration of 'l'\nnote: 'l' previously declared here");
-}
-
-TEST_F(ResolverVarLetValidationTest, GlobalVarRedeclaredAsLocal) {
-    // var v : f32 = 2.1;
-    // fn my_func() {
-    //   var v : f32 = 2.0;
-    //   return 0;
-    // }
-
-    Global("v", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
-
-    WrapInFunction(Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone, Expr(2_f)));
-
-    EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverVarLetValidationTest, VarRedeclaredInInnerBlock) {
-    // {
-    //  var v : f32;
-    //  { var v : f32; }
-    // }
-    auto* var_outer = Var("v", ty.f32(), ast::StorageClass::kNone);
-    auto* var_inner = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone);
-    auto* inner = Block(Decl(var_inner));
-    auto* outer_body = Block(Decl(var_outer), inner);
-
-    WrapInFunction(outer_body);
-
-    EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverVarLetValidationTest, VarRedeclaredInIfBlock) {
-    // {
-    //   var v : f32 = 3.14;
-    //   if (true) { var v : f32 = 2.0; }
-    // }
-    auto* var_a_float = Var("v", ty.f32(), ast::StorageClass::kNone, Expr(3.1_f));
-
-    auto* var = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone, Expr(2_f));
-
-    auto* cond = Expr(true);
-    auto* body = Block(Decl(var));
-
-    auto* outer_body = Block(Decl(var_a_float), If(cond, body));
-
-    WrapInFunction(outer_body);
-
-    EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverVarLetValidationTest, InferredPtrStorageAccessMismatch) {
-    // struct Inner {
-    //    arr: array<i32, 4>;
-    // }
-    // struct S {
-    //    inner: Inner;
-    // }
-    // @group(0) @binding(0) var<storage> s : S;
-    // fn f() {
-    //   let p : pointer<storage, i32, read_write> = &s.inner.arr[2i];
-    // }
-    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
-    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
-    auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(0),
-                               create<ast::GroupAttribute>(0),
-                           });
-
-    auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 2_i);
-    auto* ptr =
-        Let(Source{{12, 34}}, "p",
-            ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite), AddressOf(expr));
-
-    WrapInFunction(ptr);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: cannot initialize let of type "
-              "'ptr<storage, i32, read_write>' with value of type "
-              "'ptr<storage, i32, read>'");
-}
-
-TEST_F(ResolverVarLetValidationTest, NonConstructibleType_Atomic) {
-    auto* v = Var("v", ty.atomic(Source{{12, 34}}, ty.i32()));
-    WrapInFunction(v);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: function variable must have a constructible type");
-}
-
-TEST_F(ResolverVarLetValidationTest, NonConstructibleType_RuntimeArray) {
-    auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))});
-    auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
-    WrapInFunction(v);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
-56:78 note: while analysing structure member S.m
-12:34 note: while instantiating variable v)");
-}
-
-TEST_F(ResolverVarLetValidationTest, NonConstructibleType_Struct_WithAtomic) {
-    auto* s = Structure("S", {Member("m", ty.atomic(ty.i32()))});
-    auto* v = Var("v", ty.Of(s));
-    WrapInFunction(v);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "error: function variable must have a constructible type");
-}
-
-TEST_F(ResolverVarLetValidationTest, NonConstructibleType_InferredType) {
-    // @group(0) @binding(0) var s : sampler;
-    // fn foo() {
-    //   var v = s;
-    // }
-    Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 0));
-    auto* v = Var(Source{{12, 34}}, "v", nullptr, Expr("s"));
-    WrapInFunction(v);
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: function variable must have a constructible type");
-}
-
-TEST_F(ResolverVarLetValidationTest, InvalidStorageClassForInitializer) {
-    // var<workgroup> v : f32 = 1.23;
-    Global(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kWorkgroup, Expr(1.23_f));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              "12:34 error: var of storage class 'workgroup' cannot have "
-              "an initializer. var initializers are only supported for the "
-              "storage classes 'private' and 'function'");
-}
-
-TEST_F(ResolverVarLetValidationTest, VectorLetNoType) {
-    // let a : mat3x3 = mat3x3<f32>();
-    WrapInFunction(Let("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3), vec3<f32>()));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
-}
-
-TEST_F(ResolverVarLetValidationTest, VectorVarNoType) {
-    // var a : mat3x3;
-    WrapInFunction(Var("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3)));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
-}
-
-TEST_F(ResolverVarLetValidationTest, MatrixLetNoType) {
-    // let a : mat3x3 = mat3x3<f32>();
-    WrapInFunction(Let("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3), mat3x3<f32>()));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
-}
-
-TEST_F(ResolverVarLetValidationTest, MatrixVarNoType) {
-    // var a : mat3x3;
-    WrapInFunction(Var("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3)));
-
-    EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
-}
-
-}  // namespace
-}  // namespace tint::resolver
diff --git a/src/tint/resolver/variable_test.cc b/src/tint/resolver/variable_test.cc
new file mode 100644
index 0000000..7eb0dea
--- /dev/null
+++ b/src/tint/resolver/variable_test.cc
@@ -0,0 +1,1288 @@
+// Copyright 2021 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/resolver/resolver.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/reference.h"
+
+#include "gmock/gmock.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+struct ResolverVariableTest : public resolver::TestHelper, public testing::Test {};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Function-scope 'var'
+////////////////////////////////////////////////////////////////////////////////////////////////////
+TEST_F(ResolverVariableTest, LocalVar_NoConstructor) {
+    // struct S { i : i32; }
+    // alias A = S;
+    // fn F(){
+    //   var i : i32;
+    //   var u : u32;
+    //   var f : f32;
+    //   var h : f16;
+    //   var b : bool;
+    //   var s : S;
+    //   var a : A;
+    // }
+
+    Enable(ast::Extension::kF16);
+
+    auto* S = Structure("S", {Member("i", ty.i32())});
+    auto* A = Alias("A", ty.Of(S));
+
+    auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
+    auto* u = Var("u", ty.u32(), ast::StorageClass::kNone);
+    auto* f = Var("f", ty.f32(), ast::StorageClass::kNone);
+    auto* h = Var("h", ty.f16(), ast::StorageClass::kNone);
+    auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone);
+    auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone);
+    auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone);
+
+    Func("F", {}, ty.void_(),
+         {
+             Decl(i),
+             Decl(u),
+             Decl(f),
+             Decl(h),
+             Decl(b),
+             Decl(s),
+             Decl(a),
+         });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    // `var` declarations are always of reference type
+    ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(h)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
+
+    EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+    EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
+    EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(h)->As<sem::Reference>()->StoreType()->Is<sem::F16>());
+    EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
+    EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+    EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+
+    EXPECT_EQ(Sem().Get(i)->Constructor(), nullptr);
+    EXPECT_EQ(Sem().Get(u)->Constructor(), nullptr);
+    EXPECT_EQ(Sem().Get(f)->Constructor(), nullptr);
+    EXPECT_EQ(Sem().Get(h)->Constructor(), nullptr);
+    EXPECT_EQ(Sem().Get(b)->Constructor(), nullptr);
+    EXPECT_EQ(Sem().Get(s)->Constructor(), nullptr);
+    EXPECT_EQ(Sem().Get(a)->Constructor(), nullptr);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_WithConstructor) {
+    // struct S { i : i32; }
+    // alias A = S;
+    // fn F(){
+    //   var i : i32 = 1i;
+    //   var u : u32 = 1u;
+    //   var f : f32 = 1.f;
+    //   var h : f16 = 1.h;
+    //   var b : bool = true;
+    //   var s : S = S(1);
+    //   var a : A = A(1);
+    // }
+
+    Enable(ast::Extension::kF16);
+
+    auto* S = Structure("S", {Member("i", ty.i32())});
+    auto* A = Alias("A", ty.Of(S));
+
+    auto* i_c = Expr(1_i);
+    auto* u_c = Expr(1_u);
+    auto* f_c = Expr(1_f);
+    auto* h_c = Expr(1_h);
+    auto* b_c = Expr(true);
+    auto* s_c = Construct(ty.Of(S), Expr(1_i));
+    auto* a_c = Construct(ty.Of(A), Expr(1_i));
+
+    auto* i = Var("i", ty.i32(), ast::StorageClass::kNone, i_c);
+    auto* u = Var("u", ty.u32(), ast::StorageClass::kNone, u_c);
+    auto* f = Var("f", ty.f32(), ast::StorageClass::kNone, f_c);
+    auto* h = Var("h", ty.f16(), ast::StorageClass::kNone, h_c);
+    auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone, b_c);
+    auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone, s_c);
+    auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone, a_c);
+
+    Func("F", {}, ty.void_(),
+         {
+             Decl(i),
+             Decl(u),
+             Decl(f),
+             Decl(h),
+             Decl(b),
+             Decl(s),
+             Decl(a),
+         });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    // `var` declarations are always of reference type
+    ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(h)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
+
+    EXPECT_EQ(TypeOf(i)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(u)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(f)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(b)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(s)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(a)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+
+    EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+    EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
+    EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
+    EXPECT_TRUE(TypeOf(h)->As<sem::Reference>()->StoreType()->Is<sem::F16>());
+    EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
+    EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+    EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+
+    EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
+    EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
+    EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
+    EXPECT_EQ(Sem().Get(h)->Constructor()->Declaration(), h_c);
+    EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
+    EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
+    EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsAlias) {
+    // type a = i32;
+    //
+    // fn F() {
+    //   var a = false;
+    // }
+
+    auto* t = Alias("a", ty.i32());
+    auto* v = Var("a", nullptr, Expr(false));
+    Func("F", {}, ty.void_(), {Decl(v)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* type_t = Sem().Get(t);
+    auto* local = Sem().Get<sem::LocalVariable>(v);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), type_t);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsStruct) {
+    // struct a {
+    //   m : i32;
+    // };
+    //
+    // fn F() {
+    //   var a = true;
+    // }
+
+    auto* t = Structure("a", {Member("m", ty.i32())});
+    auto* v = Var("a", nullptr, Expr(false));
+    Func("F", {}, ty.void_(), {Decl(v)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* type_t = Sem().Get(t);
+    auto* local = Sem().Get<sem::LocalVariable>(v);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), type_t);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsFunction) {
+    // fn a() {
+    //   var a = true;
+    // }
+
+    auto* v = Var("a", nullptr, Expr(false));
+    auto* f = Func("a", {}, ty.void_(), {Decl(v)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* func = Sem().Get(f);
+    ASSERT_NE(func, nullptr);
+
+    auto* local = Sem().Get<sem::LocalVariable>(v);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), func);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsGlobalVar) {
+    // var<private> a : i32;
+    //
+    // fn F() {
+    //   var a = a;
+    // }
+
+    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = Var("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(v)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* local = Sem().Get<sem::LocalVariable>(v);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), global);
+
+    auto* user_v = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user_v, nullptr);
+    EXPECT_EQ(user_v->Variable(), global);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsGlobalConst) {
+    // const a : i32 = 1i;
+    //
+    // fn X() {
+    //   var a = (a == 123);
+    // }
+
+    auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
+    auto* v = Var("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(v)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* local = Sem().Get<sem::LocalVariable>(v);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), global);
+
+    auto* user_v = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user_v, nullptr);
+    EXPECT_EQ(user_v->Variable(), global);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsLocalVar) {
+    // fn F() {
+    //   var a : i32 = 1i; // x
+    //   {
+    //     var a = a; // y
+    //   }
+    // }
+
+    auto* x = Var("a", ty.i32(), Expr(1_i));
+    auto* y = Var("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_x = Sem().Get<sem::LocalVariable>(x);
+    auto* local_y = Sem().Get<sem::LocalVariable>(y);
+
+    ASSERT_NE(local_x, nullptr);
+    ASSERT_NE(local_y, nullptr);
+    EXPECT_EQ(local_y->Shadows(), local_x);
+
+    auto* user_y = Sem().Get<sem::VariableUser>(local_y->Declaration()->constructor);
+    ASSERT_NE(user_y, nullptr);
+    EXPECT_EQ(user_y->Variable(), local_x);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsLocalConst) {
+    // fn F() {
+    //   const a : i32 = 1i;
+    //   {
+    //     var a = (a == 123);
+    //   }
+    // }
+
+    auto* c = Const("a", ty.i32(), Expr(1_i));
+    auto* v = Var("a", nullptr, Expr("a"));
+    Func("X", {}, ty.void_(), {Decl(c), Block(Decl(v))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_c = Sem().Get<sem::LocalVariable>(c);
+    auto* local_v = Sem().Get<sem::LocalVariable>(v);
+
+    ASSERT_NE(local_c, nullptr);
+    ASSERT_NE(local_v, nullptr);
+    EXPECT_EQ(local_v->Shadows(), local_c);
+
+    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+    ASSERT_NE(user_v, nullptr);
+    EXPECT_EQ(user_v->Variable(), local_c);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsLocalLet) {
+    // fn F() {
+    //   let a : i32 = 1i;
+    //   {
+    //     var a = (a == 123);
+    //   }
+    // }
+
+    auto* l = Let("a", ty.i32(), Expr(1_i));
+    auto* v = Var("a", nullptr, Expr("a"));
+    Func("X", {}, ty.void_(), {Decl(l), Block(Decl(v))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_l = Sem().Get<sem::LocalVariable>(l);
+    auto* local_v = Sem().Get<sem::LocalVariable>(v);
+
+    ASSERT_NE(local_l, nullptr);
+    ASSERT_NE(local_v, nullptr);
+    EXPECT_EQ(local_v->Shadows(), local_l);
+
+    auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+    ASSERT_NE(user_v, nullptr);
+    EXPECT_EQ(user_v->Variable(), local_l);
+}
+
+TEST_F(ResolverVariableTest, LocalVar_ShadowsParam) {
+    // fn F(a : i32) {
+    //   {
+    //     var a = a;
+    //   }
+    // }
+
+    auto* p = Param("a", ty.i32());
+    auto* v = Var("a", nullptr, Expr("a"));
+    Func("X", {p}, ty.void_(), {Block(Decl(v))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* param = Sem().Get<sem::Parameter>(p);
+    auto* local = Sem().Get<sem::LocalVariable>(v);
+
+    ASSERT_NE(param, nullptr);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), param);
+
+    auto* user_v = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user_v, nullptr);
+    EXPECT_EQ(user_v->Variable(), param);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Function-scope 'let'
+////////////////////////////////////////////////////////////////////////////////////////////////////
+TEST_F(ResolverVariableTest, LocalLet) {
+    // struct S { i : i32; }
+    // fn F(){
+    //   var v : i32;
+    //   let i : i32 = 1i;
+    //   let u : u32 = 1u;
+    //   let f : f32 = 1.f;
+    //   let h : h32 = 1.h;
+    //   let b : bool = true;
+    //   let s : S = S(1);
+    //   let a : A = A(1);
+    //   let p : pointer<function, i32> = &v;
+    // }
+
+    Enable(ast::Extension::kF16);
+
+    auto* S = Structure("S", {Member("i", ty.i32())});
+    auto* A = Alias("A", ty.Of(S));
+    auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
+
+    auto* i_c = Expr(1_i);
+    auto* u_c = Expr(1_u);
+    auto* f_c = Expr(1_f);
+    auto* h_c = Expr(1_h);
+    auto* b_c = Expr(true);
+    auto* s_c = Construct(ty.Of(S), Expr(1_i));
+    auto* a_c = Construct(ty.Of(A), Expr(1_i));
+    auto* p_c = AddressOf(v);
+
+    auto* i = Let("i", ty.i32(), i_c);
+    auto* u = Let("u", ty.u32(), u_c);
+    auto* f = Let("f", ty.f32(), f_c);
+    auto* h = Let("h", ty.f16(), h_c);
+    auto* b = Let("b", ty.bool_(), b_c);
+    auto* s = Let("s", ty.Of(S), s_c);
+    auto* a = Let("a", ty.Of(A), a_c);
+    auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
+
+    Func("F", {}, ty.void_(),
+         {
+             Decl(v),
+             Decl(i),
+             Decl(u),
+             Decl(f),
+             Decl(h),
+             Decl(b),
+             Decl(s),
+             Decl(a),
+             Decl(p),
+         });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    // `let` declarations are always of the storage type
+    ASSERT_TRUE(TypeOf(i)->Is<sem::I32>());
+    ASSERT_TRUE(TypeOf(u)->Is<sem::U32>());
+    ASSERT_TRUE(TypeOf(f)->Is<sem::F32>());
+    ASSERT_TRUE(TypeOf(h)->Is<sem::F16>());
+    ASSERT_TRUE(TypeOf(b)->Is<sem::Bool>());
+    ASSERT_TRUE(TypeOf(s)->Is<sem::Struct>());
+    ASSERT_TRUE(TypeOf(a)->Is<sem::Struct>());
+    ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
+    ASSERT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
+
+    EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
+    EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
+    EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
+    EXPECT_EQ(Sem().Get(h)->Constructor()->Declaration(), h_c);
+    EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
+    EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
+    EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
+    EXPECT_EQ(Sem().Get(p)->Constructor()->Declaration(), p_c);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_InheritsAccessFromOriginatingVariable) {
+    // struct Inner {
+    //    arr: array<i32, 4>;
+    // }
+    // struct S {
+    //    inner: Inner;
+    // }
+    // @group(0) @binding(0) var<storage, read_write> s : S;
+    // fn f() {
+    //   let p = &s.inner.arr[4];
+    // }
+    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
+    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+    auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(0u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
+
+    auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4_i);
+    auto* ptr = Let("p", nullptr, AddressOf(expr));
+
+    WrapInFunction(ptr);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
+
+    EXPECT_EQ(TypeOf(expr)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsAlias) {
+    // type a = i32;
+    //
+    // fn F() {
+    //   let a = true;
+    // }
+
+    auto* t = Alias("a", ty.i32());
+    auto* l = Let("a", nullptr, Expr(false));
+    Func("F", {}, ty.void_(), {Decl(l)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* type_t = Sem().Get(t);
+    auto* local = Sem().Get<sem::LocalVariable>(l);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), type_t);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsStruct) {
+    // struct a {
+    //   m : i32;
+    // };
+    //
+    // fn F() {
+    //   let a = false;
+    // }
+
+    auto* t = Structure("a", {Member("m", ty.i32())});
+    auto* l = Let("a", nullptr, Expr(false));
+    Func("F", {}, ty.void_(), {Decl(l)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* type_t = Sem().Get(t);
+    auto* local = Sem().Get<sem::LocalVariable>(l);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), type_t);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsFunction) {
+    // fn a() {
+    //   let a = false;
+    // }
+
+    auto* l = Let("a", nullptr, Expr(false));
+    auto* fb = Func("a", {}, ty.void_(), {Decl(l)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* func = Sem().Get(fb);
+    ASSERT_NE(func, nullptr);
+
+    auto* local = Sem().Get<sem::LocalVariable>(l);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), func);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsGlobalVar) {
+    // var<private> a : i32;
+    //
+    // fn F() {
+    //   let a = a;
+    // }
+
+    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* l = Let("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(l)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* local = Sem().Get<sem::LocalVariable>(l);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), global);
+
+    auto* user = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), global);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsGlobalConst) {
+    // const a : i32 = 1i;
+    //
+    // fn F() {
+    //   let a = a;
+    // }
+
+    auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
+    auto* l = Let("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(l)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* local = Sem().Get<sem::LocalVariable>(l);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), global);
+
+    auto* user = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), global);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsLocalVar) {
+    // fn F() {
+    //   var a : i32 = 1i;
+    //   {
+    //     let a = a;
+    //   }
+    // }
+
+    auto* v = Var("a", ty.i32(), Expr(1_i));
+    auto* l = Let("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(v), Block(Decl(l))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_v = Sem().Get<sem::LocalVariable>(v);
+    auto* local_l = Sem().Get<sem::LocalVariable>(l);
+
+    ASSERT_NE(local_v, nullptr);
+    ASSERT_NE(local_l, nullptr);
+    EXPECT_EQ(local_l->Shadows(), local_v);
+
+    auto* user = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), local_v);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsLocalConst) {
+    // fn X() {
+    //   const a : i32 = 1i; // x
+    //   {
+    //     let a = a; // y
+    //   }
+    // }
+
+    auto* x = Const("a", ty.i32(), Expr(1_i));
+    auto* y = Let("a", nullptr, Expr("a"));
+    Func("X", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_x = Sem().Get<sem::LocalVariable>(x);
+    auto* local_y = Sem().Get<sem::LocalVariable>(y);
+
+    ASSERT_NE(local_x, nullptr);
+    ASSERT_NE(local_y, nullptr);
+    EXPECT_EQ(local_y->Shadows(), local_x);
+
+    auto* user = Sem().Get<sem::VariableUser>(local_y->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), local_x);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsLocalLet) {
+    // fn X() {
+    //   let a : i32 = 1i; // x
+    //   {
+    //     let a = a; // y
+    //   }
+    // }
+
+    auto* x = Let("a", ty.i32(), Expr(1_i));
+    auto* y = Let("a", nullptr, Expr("a"));
+    Func("X", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_x = Sem().Get<sem::LocalVariable>(x);
+    auto* local_y = Sem().Get<sem::LocalVariable>(y);
+
+    ASSERT_NE(local_x, nullptr);
+    ASSERT_NE(local_y, nullptr);
+    EXPECT_EQ(local_y->Shadows(), local_x);
+
+    auto* user = Sem().Get<sem::VariableUser>(local_y->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), local_x);
+}
+
+TEST_F(ResolverVariableTest, LocalLet_ShadowsParam) {
+    // fn F(a : i32) {
+    //   {
+    //     let a = a;
+    //   }
+    // }
+
+    auto* p = Param("a", ty.i32());
+    auto* l = Let("a", nullptr, Expr("a"));
+    Func("X", {p}, ty.void_(), {Block(Decl(l))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* param = Sem().Get<sem::Parameter>(p);
+    auto* local = Sem().Get<sem::LocalVariable>(l);
+
+    ASSERT_NE(param, nullptr);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), param);
+
+    auto* user = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), param);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Function-scope const
+////////////////////////////////////////////////////////////////////////////////////////////////////
+TEST_F(ResolverVariableTest, LocalConst_ShadowsAlias) {
+    // type a = i32;
+    //
+    // fn F() {
+    //   const a = true;
+    // }
+
+    auto* t = Alias("a", ty.i32());
+    auto* c = Const("a", nullptr, Expr(false));
+    Func("F", {}, ty.void_(), {Decl(c)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* type_t = Sem().Get(t);
+    auto* local = Sem().Get<sem::LocalVariable>(c);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), type_t);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsStruct) {
+    // struct a {
+    //   m : i32;
+    // };
+    //
+    // fn F() {
+    //   const a = false;
+    // }
+
+    auto* t = Structure("a", {Member("m", ty.i32())});
+    auto* c = Const("a", nullptr, Expr(false));
+    Func("F", {}, ty.void_(), {Decl(c)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* type_t = Sem().Get(t);
+    auto* local = Sem().Get<sem::LocalVariable>(c);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), type_t);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsFunction) {
+    // fn a() {
+    //   const a = false;
+    // }
+
+    auto* c = Const("a", nullptr, Expr(false));
+    auto* fb = Func("a", {}, ty.void_(), {Decl(c)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* func = Sem().Get(fb);
+    ASSERT_NE(func, nullptr);
+
+    auto* local = Sem().Get<sem::LocalVariable>(c);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), func);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsGlobalVar) {
+    // var<private> a : i32;
+    //
+    // fn F() {
+    //   const a = 1i;
+    // }
+
+    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* c = Const("a", nullptr, Expr(1_i));
+    Func("F", {}, ty.void_(), {Decl(c)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* local = Sem().Get<sem::LocalVariable>(c);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), global);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsGlobalConst) {
+    // const a : i32 = 1i;
+    //
+    // fn F() {
+    //   const a = a;
+    // }
+
+    auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
+    auto* c = Const("a", nullptr, Expr("a"));
+    Func("F", {}, ty.void_(), {Decl(c)});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* local = Sem().Get<sem::LocalVariable>(c);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), global);
+
+    auto* user = Sem().Get<sem::VariableUser>(local->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), global);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsLocalVar) {
+    // fn F() {
+    //   var a = 1i;
+    //   {
+    //     const a = 1i;
+    //   }
+    // }
+
+    auto* v = Var("a", ty.i32(), Expr(1_i));
+    auto* c = Const("a", nullptr, Expr(1_i));
+    Func("F", {}, ty.void_(), {Decl(v), Block(Decl(c))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_v = Sem().Get<sem::LocalVariable>(v);
+    auto* local_c = Sem().Get<sem::LocalVariable>(c);
+
+    ASSERT_NE(local_v, nullptr);
+    ASSERT_NE(local_c, nullptr);
+    EXPECT_EQ(local_c->Shadows(), local_v);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsLocalConst) {
+    // fn X() {
+    //   const a = 1i; // x
+    //   {
+    //     const a = a; // y
+    //   }
+    // }
+
+    auto* x = Const("a", ty.i32(), Expr(1_i));
+    auto* y = Const("a", nullptr, Expr("a"));
+    Func("X", {}, ty.void_(), {Decl(x), Block(Decl(y))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_x = Sem().Get<sem::LocalVariable>(x);
+    auto* local_y = Sem().Get<sem::LocalVariable>(y);
+
+    ASSERT_NE(local_x, nullptr);
+    ASSERT_NE(local_y, nullptr);
+    EXPECT_EQ(local_y->Shadows(), local_x);
+
+    auto* user = Sem().Get<sem::VariableUser>(local_y->Declaration()->constructor);
+    ASSERT_NE(user, nullptr);
+    EXPECT_EQ(user->Variable(), local_x);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsLocalLet) {
+    // fn X() {
+    //   let a = 1i; // x
+    //   {
+    //     const a = 1i; // y
+    //   }
+    // }
+
+    auto* l = Let("a", ty.i32(), Expr(1_i));
+    auto* c = Const("a", nullptr, Expr(1_i));
+    Func("X", {}, ty.void_(), {Decl(l), Block(Decl(c))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* local_l = Sem().Get<sem::LocalVariable>(l);
+    auto* local_c = Sem().Get<sem::LocalVariable>(c);
+
+    ASSERT_NE(local_l, nullptr);
+    ASSERT_NE(local_c, nullptr);
+    EXPECT_EQ(local_c->Shadows(), local_l);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ShadowsParam) {
+    // fn F(a : i32) {
+    //   {
+    //     const a = 1i;
+    //   }
+    // }
+
+    auto* p = Param("a", ty.i32());
+    auto* c = Const("a", nullptr, Expr(1_i));
+    Func("X", {p}, ty.void_(), {Block(Decl(c))});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* param = Sem().Get<sem::Parameter>(p);
+    auto* local = Sem().Get<sem::LocalVariable>(c);
+
+    ASSERT_NE(param, nullptr);
+    ASSERT_NE(local, nullptr);
+    EXPECT_EQ(local->Shadows(), param);
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ExplicitType_Decls) {
+    auto* c_i32 = Const("a", ty.i32(), Expr(0_i));
+    auto* c_u32 = Const("b", ty.u32(), Expr(0_u));
+    auto* c_f32 = Const("c", ty.f32(), Expr(0_f));
+    auto* c_vi32 = Const("d", ty.vec3<i32>(), vec3<i32>());
+    auto* c_vu32 = Const("e", ty.vec3<u32>(), vec3<u32>());
+    auto* c_vf32 = Const("f", ty.vec3<f32>(), vec3<f32>());
+    auto* c_mf32 = Const("g", ty.mat3x3<f32>(), mat3x3<f32>());
+
+    WrapInFunction(c_i32, c_u32, c_f32, c_vi32, c_vu32, c_vf32, c_mf32);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    EXPECT_EQ(Sem().Get(c_i32)->Declaration(), c_i32);
+    EXPECT_EQ(Sem().Get(c_u32)->Declaration(), c_u32);
+    EXPECT_EQ(Sem().Get(c_f32)->Declaration(), c_f32);
+    EXPECT_EQ(Sem().Get(c_vi32)->Declaration(), c_vi32);
+    EXPECT_EQ(Sem().Get(c_vu32)->Declaration(), c_vu32);
+    EXPECT_EQ(Sem().Get(c_vf32)->Declaration(), c_vf32);
+    EXPECT_EQ(Sem().Get(c_mf32)->Declaration(), c_mf32);
+
+    ASSERT_TRUE(TypeOf(c_i32)->Is<sem::I32>());
+    ASSERT_TRUE(TypeOf(c_u32)->Is<sem::U32>());
+    ASSERT_TRUE(TypeOf(c_f32)->Is<sem::F32>());
+    ASSERT_TRUE(TypeOf(c_vi32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vu32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vf32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_mf32)->Is<sem::Matrix>());
+
+    EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero());
+}
+
+TEST_F(ResolverVariableTest, LocalConst_ImplicitType_Decls) {
+    auto* c_i32 = Const("a", nullptr, Expr(0_i));
+    auto* c_u32 = Const("b", nullptr, Expr(0_u));
+    auto* c_f32 = Const("c", nullptr, Expr(0_f));
+    auto* c_ai = Const("d", nullptr, Expr(0_a));
+    auto* c_af = Const("e", nullptr, Expr(0._a));
+    auto* c_vi32 = Const("f", nullptr, vec3<i32>());
+    auto* c_vu32 = Const("g", nullptr, vec3<u32>());
+    auto* c_vf32 = Const("h", nullptr, vec3<f32>());
+    auto* c_vai = Const("i", nullptr, Construct(ty.vec(nullptr, 3), Expr(0_a)));
+    auto* c_vaf = Const("j", nullptr, Construct(ty.vec(nullptr, 3), Expr(0._a)));
+    auto* c_mf32 = Const("k", nullptr, mat3x3<f32>());
+    auto* c_maf32 = Const("l", nullptr,
+                          Construct(ty.mat(nullptr, 3, 3),  //
+                                    Construct(ty.vec(nullptr, 3), Expr(0._a)),
+                                    Construct(ty.vec(nullptr, 3), Expr(0._a)),
+                                    Construct(ty.vec(nullptr, 3), Expr(0._a))));
+
+    WrapInFunction(c_i32, c_u32, c_f32, c_ai, c_af, c_vi32, c_vu32, c_vf32, c_vai, c_vaf, c_mf32,
+                   c_maf32);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    EXPECT_EQ(Sem().Get(c_i32)->Declaration(), c_i32);
+    EXPECT_EQ(Sem().Get(c_u32)->Declaration(), c_u32);
+    EXPECT_EQ(Sem().Get(c_f32)->Declaration(), c_f32);
+    EXPECT_EQ(Sem().Get(c_ai)->Declaration(), c_ai);
+    EXPECT_EQ(Sem().Get(c_af)->Declaration(), c_af);
+    EXPECT_EQ(Sem().Get(c_vi32)->Declaration(), c_vi32);
+    EXPECT_EQ(Sem().Get(c_vu32)->Declaration(), c_vu32);
+    EXPECT_EQ(Sem().Get(c_vf32)->Declaration(), c_vf32);
+    EXPECT_EQ(Sem().Get(c_vai)->Declaration(), c_vai);
+    EXPECT_EQ(Sem().Get(c_vaf)->Declaration(), c_vaf);
+    EXPECT_EQ(Sem().Get(c_mf32)->Declaration(), c_mf32);
+    EXPECT_EQ(Sem().Get(c_maf32)->Declaration(), c_maf32);
+
+    ASSERT_TRUE(TypeOf(c_i32)->Is<sem::I32>());
+    ASSERT_TRUE(TypeOf(c_u32)->Is<sem::U32>());
+    ASSERT_TRUE(TypeOf(c_f32)->Is<sem::F32>());
+    ASSERT_TRUE(TypeOf(c_ai)->Is<sem::AbstractInt>());
+    ASSERT_TRUE(TypeOf(c_af)->Is<sem::AbstractFloat>());
+    ASSERT_TRUE(TypeOf(c_vi32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vu32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vf32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vai)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vaf)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_mf32)->Is<sem::Matrix>());
+    ASSERT_TRUE(TypeOf(c_maf32)->Is<sem::Matrix>());
+
+    EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_ai)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_af)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vai)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vaf)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_maf32)->ConstantValue()->AllZero());
+}
+
+TEST_F(ResolverVariableTest, LocalConst_PropagateConstValue) {
+    auto* a = Const("a", nullptr, Expr(42_i));
+    auto* b = Const("b", nullptr, Expr("a"));
+    auto* c = Const("c", nullptr, Expr("b"));
+
+    WrapInFunction(a, b, c);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(c)->Is<sem::I32>());
+
+    EXPECT_EQ(Sem().Get(c)->ConstantValue()->As<i32>(), 42_i);
+}
+
+// Enable when we have @const operators implemented
+TEST_F(ResolverVariableTest, DISABLED_LocalConst_ConstEval) {
+    auto* c = Const("c", nullptr, Div(Mul(Add(1_i, 2_i), 3_i), 2_i));
+
+    WrapInFunction(c);
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(c)->Is<sem::I32>());
+
+    EXPECT_EQ(Sem().Get(c)->ConstantValue()->As<i32>(), 3_i);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Module-scope 'var'
+////////////////////////////////////////////////////////////////////////////////////////////////////
+TEST_F(ResolverVariableTest, GlobalVar_StorageClass) {
+    // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
+
+    auto* buf = Structure("S", {Member("m", ty.i32())});
+    auto* private_ = GlobalVar("p", ty.i32(), ast::StorageClass::kPrivate);
+    auto* workgroup = GlobalVar("w", ty.i32(), ast::StorageClass::kWorkgroup);
+    auto* uniform = GlobalVar("ub", ty.Of(buf), ast::StorageClass::kUniform,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(0u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
+    auto* storage = GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(1u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
+    auto* handle = GlobalVar("h", ty.depth_texture(ast::TextureDimension::k2d),
+                             ast::AttributeList{
+                                 create<ast::BindingAttribute>(2u),
+                                 create<ast::GroupAttribute>(0u),
+                             });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(workgroup)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(uniform)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
+    ASSERT_TRUE(TypeOf(handle)->Is<sem::Reference>());
+
+    EXPECT_EQ(TypeOf(private_)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(workgroup)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+    EXPECT_EQ(TypeOf(uniform)->As<sem::Reference>()->Access(), ast::Access::kRead);
+    EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), ast::Access::kRead);
+    EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
+}
+
+TEST_F(ResolverVariableTest, GlobalVar_ExplicitStorageClass) {
+    // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
+
+    auto* buf = Structure("S", {Member("m", ty.i32())});
+    auto* storage =
+        GlobalVar("sb", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                  ast::AttributeList{
+                      create<ast::BindingAttribute>(1u),
+                      create<ast::GroupAttribute>(0u),
+                  });
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
+
+    EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Module-scope const
+////////////////////////////////////////////////////////////////////////////////////////////////////
+TEST_F(ResolverVariableTest, GlobalConst_ExplicitType_Decls) {
+    auto* c_i32 = GlobalConst("a", ty.i32(), Expr(0_i));
+    auto* c_u32 = GlobalConst("b", ty.u32(), Expr(0_u));
+    auto* c_f32 = GlobalConst("c", ty.f32(), Expr(0_f));
+    auto* c_vi32 = GlobalConst("d", ty.vec3<i32>(), vec3<i32>());
+    auto* c_vu32 = GlobalConst("e", ty.vec3<u32>(), vec3<u32>());
+    auto* c_vf32 = GlobalConst("f", ty.vec3<f32>(), vec3<f32>());
+    auto* c_mf32 = GlobalConst("g", ty.mat3x3<f32>(), mat3x3<f32>());
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    EXPECT_EQ(Sem().Get(c_i32)->Declaration(), c_i32);
+    EXPECT_EQ(Sem().Get(c_u32)->Declaration(), c_u32);
+    EXPECT_EQ(Sem().Get(c_f32)->Declaration(), c_f32);
+    EXPECT_EQ(Sem().Get(c_vi32)->Declaration(), c_vi32);
+    EXPECT_EQ(Sem().Get(c_vu32)->Declaration(), c_vu32);
+    EXPECT_EQ(Sem().Get(c_vf32)->Declaration(), c_vf32);
+    EXPECT_EQ(Sem().Get(c_mf32)->Declaration(), c_mf32);
+
+    ASSERT_TRUE(TypeOf(c_i32)->Is<sem::I32>());
+    ASSERT_TRUE(TypeOf(c_u32)->Is<sem::U32>());
+    ASSERT_TRUE(TypeOf(c_f32)->Is<sem::F32>());
+    ASSERT_TRUE(TypeOf(c_vi32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vu32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vf32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_mf32)->Is<sem::Matrix>());
+
+    EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero());
+}
+
+TEST_F(ResolverVariableTest, GlobalConst_ImplicitType_Decls) {
+    auto* c_i32 = GlobalConst("a", nullptr, Expr(0_i));
+    auto* c_u32 = GlobalConst("b", nullptr, Expr(0_u));
+    auto* c_f32 = GlobalConst("c", nullptr, Expr(0_f));
+    auto* c_ai = GlobalConst("d", nullptr, Expr(0_a));
+    auto* c_af = GlobalConst("e", nullptr, Expr(0._a));
+    auto* c_vi32 = GlobalConst("f", nullptr, vec3<i32>());
+    auto* c_vu32 = GlobalConst("g", nullptr, vec3<u32>());
+    auto* c_vf32 = GlobalConst("h", nullptr, vec3<f32>());
+    auto* c_vai = GlobalConst("i", nullptr, Construct(ty.vec(nullptr, 3), Expr(0_a)));
+    auto* c_vaf = GlobalConst("j", nullptr, Construct(ty.vec(nullptr, 3), Expr(0._a)));
+    auto* c_mf32 = GlobalConst("k", nullptr, mat3x3<f32>());
+    auto* c_maf32 = GlobalConst("l", nullptr,
+                                Construct(ty.mat(nullptr, 3, 3),  //
+                                          Construct(ty.vec(nullptr, 3), Expr(0._a)),
+                                          Construct(ty.vec(nullptr, 3), Expr(0._a)),
+                                          Construct(ty.vec(nullptr, 3), Expr(0._a))));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    EXPECT_EQ(Sem().Get(c_i32)->Declaration(), c_i32);
+    EXPECT_EQ(Sem().Get(c_u32)->Declaration(), c_u32);
+    EXPECT_EQ(Sem().Get(c_f32)->Declaration(), c_f32);
+    EXPECT_EQ(Sem().Get(c_ai)->Declaration(), c_ai);
+    EXPECT_EQ(Sem().Get(c_af)->Declaration(), c_af);
+    EXPECT_EQ(Sem().Get(c_vi32)->Declaration(), c_vi32);
+    EXPECT_EQ(Sem().Get(c_vu32)->Declaration(), c_vu32);
+    EXPECT_EQ(Sem().Get(c_vf32)->Declaration(), c_vf32);
+    EXPECT_EQ(Sem().Get(c_vai)->Declaration(), c_vai);
+    EXPECT_EQ(Sem().Get(c_vaf)->Declaration(), c_vaf);
+    EXPECT_EQ(Sem().Get(c_mf32)->Declaration(), c_mf32);
+    EXPECT_EQ(Sem().Get(c_maf32)->Declaration(), c_maf32);
+
+    ASSERT_TRUE(TypeOf(c_i32)->Is<sem::I32>());
+    ASSERT_TRUE(TypeOf(c_u32)->Is<sem::U32>());
+    ASSERT_TRUE(TypeOf(c_f32)->Is<sem::F32>());
+    ASSERT_TRUE(TypeOf(c_ai)->Is<sem::AbstractInt>());
+    ASSERT_TRUE(TypeOf(c_af)->Is<sem::AbstractFloat>());
+    ASSERT_TRUE(TypeOf(c_vi32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vu32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vf32)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vai)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_vaf)->Is<sem::Vector>());
+    ASSERT_TRUE(TypeOf(c_mf32)->Is<sem::Matrix>());
+    ASSERT_TRUE(TypeOf(c_maf32)->Is<sem::Matrix>());
+
+    EXPECT_TRUE(Sem().Get(c_i32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_u32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_f32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_ai)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_af)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vi32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vu32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vf32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vai)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_vaf)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_mf32)->ConstantValue()->AllZero());
+    EXPECT_TRUE(Sem().Get(c_maf32)->ConstantValue()->AllZero());
+}
+
+TEST_F(ResolverVariableTest, GlobalConst_PropagateConstValue) {
+    GlobalConst("b", nullptr, Expr("a"));
+    auto* c = GlobalConst("c", nullptr, Expr("b"));
+    GlobalConst("a", nullptr, Expr(42_i));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(c)->Is<sem::I32>());
+
+    EXPECT_EQ(Sem().Get(c)->ConstantValue()->As<i32>(), 42_i);
+}
+
+// Enable when we have @const operators implemented
+TEST_F(ResolverVariableTest, DISABLED_GlobalConst_ConstEval) {
+    auto* c = GlobalConst("c", nullptr, Div(Mul(Add(1_i, 2_i), 3_i), 2_i));
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    ASSERT_TRUE(TypeOf(c)->Is<sem::I32>());
+
+    EXPECT_EQ(Sem().Get(c)->ConstantValue()->As<i32>(), 3_i);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Function parameter
+////////////////////////////////////////////////////////////////////////////////////////////////////
+TEST_F(ResolverVariableTest, Param_ShadowsFunction) {
+    // fn a(a : bool) {
+    // }
+
+    auto* p = Param("a", ty.bool_());
+    auto* f = Func("a", {p}, ty.void_(), {});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* func = Sem().Get(f);
+    auto* param = Sem().Get<sem::Parameter>(p);
+
+    ASSERT_NE(func, nullptr);
+    ASSERT_NE(param, nullptr);
+
+    EXPECT_EQ(param->Shadows(), func);
+}
+
+TEST_F(ResolverVariableTest, Param_ShadowsGlobalVar) {
+    // var<private> a : i32;
+    //
+    // fn F(a : bool) {
+    // }
+
+    auto* g = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* p = Param("a", ty.bool_());
+    Func("F", {p}, ty.void_(), {});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* param = Sem().Get<sem::Parameter>(p);
+
+    ASSERT_NE(global, nullptr);
+    ASSERT_NE(param, nullptr);
+
+    EXPECT_EQ(param->Shadows(), global);
+}
+
+TEST_F(ResolverVariableTest, Param_ShadowsGlobalConst) {
+    // const a : i32 = 1i;
+    //
+    // fn F(a : bool) {
+    // }
+
+    auto* g = GlobalConst("a", ty.i32(), Expr(1_i));
+    auto* p = Param("a", ty.bool_());
+    Func("F", {p}, ty.void_(), {});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* global = Sem().Get(g);
+    auto* param = Sem().Get<sem::Parameter>(p);
+
+    ASSERT_NE(global, nullptr);
+    ASSERT_NE(param, nullptr);
+
+    EXPECT_EQ(param->Shadows(), global);
+}
+
+TEST_F(ResolverVariableTest, Param_ShadowsAlias) {
+    // type a = i32;
+    //
+    // fn F(a : a) {
+    // }
+
+    auto* a = Alias("a", ty.i32());
+    auto* p = Param("a", ty.type_name("a"));
+    Func("F", {p}, ty.void_(), {});
+
+    ASSERT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* alias = Sem().Get(a);
+    auto* param = Sem().Get<sem::Parameter>(p);
+
+    ASSERT_NE(alias, nullptr);
+    ASSERT_NE(param, nullptr);
+
+    EXPECT_EQ(param->Shadows(), alias);
+    EXPECT_EQ(param->Type(), alias);
+}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
new file mode 100644
index 0000000..79b269c
--- /dev/null
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -0,0 +1,413 @@
+// Copyright 2021 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/resolver/resolver.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+
+#include "gmock/gmock.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+struct ResolverVariableValidationTest : public resolver::TestHelper, public testing::Test {};
+
+TEST_F(ResolverVariableValidationTest, VarNoInitializerNoType) {
+    // var a;
+    WrapInFunction(Var(Source{{12, 34}}, "a", nullptr));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: var declaration requires a type or initializer");
+}
+
+TEST_F(ResolverVariableValidationTest, GlobalVarNoInitializerNoType) {
+    // var a;
+    GlobalVar(Source{{12, 34}}, "a", nullptr);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: var declaration requires a type or initializer");
+}
+
+TEST_F(ResolverVariableValidationTest, GlobalVarUsedAtModuleScope) {
+    // var<private> a : i32;
+    // var<private> b : i32 = a;
+    GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kPrivate, nullptr);
+    GlobalVar("b", ty.i32(), ast::StorageClass::kPrivate, Expr(Source{{56, 78}}, "a"));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(56:78 error: var 'a' cannot not be referenced at module-scope
+12:34 note: var 'a' declared here)");
+}
+
+TEST_F(ResolverVariableValidationTest, OverrideNoInitializerNoType) {
+    // override a;
+    Override(Source{{12, 34}}, "a", nullptr, nullptr);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: override declaration requires a type or initializer");
+}
+
+TEST_F(ResolverVariableValidationTest, VarTypeNotStorable) {
+    // var i : i32;
+    // var p : pointer<function, i32> = &v;
+    auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
+    auto* p = Var(Source{{56, 78}}, "a", ty.pointer<i32>(ast::StorageClass::kFunction),
+                  ast::StorageClass::kNone, AddressOf(Source{{12, 34}}, "i"));
+    WrapInFunction(i, p);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "56:78 error: ptr<function, i32, read_write> cannot be used as the "
+              "type of a var");
+}
+
+TEST_F(ResolverVariableValidationTest, LetTypeNotConstructible) {
+    // @group(0) @binding(0) var t1 : texture_2d<f32>;
+    // let t2 : t1;
+    auto* t1 = GlobalVar("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+                         GroupAndBinding(0, 0));
+    auto* t2 = Let(Source{{56, 78}}, "t2", nullptr, Expr(t1));
+    WrapInFunction(t2);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "56:78 error: texture_2d<f32> cannot be used as the type of a 'let'");
+}
+
+TEST_F(ResolverVariableValidationTest, OverrideExplicitTypeNotScalar) {
+    // override o : vec3<f32>;
+    Override(Source{{56, 78}}, "o", ty.vec3<f32>(), nullptr);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "56:78 error: vec3<f32> cannot be used as the type of a 'override'");
+}
+
+TEST_F(ResolverVariableValidationTest, OverrideInferedTypeNotScalar) {
+    // override o = vec3(1.0f);
+    Override(Source{{56, 78}}, "o", nullptr, vec3<f32>(1.0_f));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "56:78 error: vec3<f32> cannot be used as the type of a 'override'");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstConstructorWrongType) {
+    // const c : i32 = 2u
+    WrapInFunction(Const(Source{{3, 3}}, "c", ty.i32(), Expr(2_u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(3:3 error: cannot initialize const of type 'i32' with value of type 'u32')");
+}
+
+TEST_F(ResolverVariableValidationTest, LetConstructorWrongType) {
+    // var v : i32 = 2u
+    WrapInFunction(Let(Source{{3, 3}}, "v", ty.i32(), Expr(2_u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
+}
+
+TEST_F(ResolverVariableValidationTest, VarConstructorWrongType) {
+    // var v : i32 = 2u
+    WrapInFunction(Var(Source{{3, 3}}, "v", ty.i32(), ast::StorageClass::kNone, Expr(2_u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstConstructorWrongTypeViaAlias) {
+    auto* a = Alias("I32", ty.i32());
+    WrapInFunction(Const(Source{{3, 3}}, "v", ty.Of(a), Expr(2_u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(3:3 error: cannot initialize const of type 'i32' with value of type 'u32')");
+}
+
+TEST_F(ResolverVariableValidationTest, LetConstructorWrongTypeViaAlias) {
+    auto* a = Alias("I32", ty.i32());
+    WrapInFunction(Let(Source{{3, 3}}, "v", ty.Of(a), Expr(2_u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
+}
+
+TEST_F(ResolverVariableValidationTest, VarConstructorWrongTypeViaAlias) {
+    auto* a = Alias("I32", ty.i32());
+    WrapInFunction(Var(Source{{3, 3}}, "v", ty.Of(a), ast::StorageClass::kNone, Expr(2_u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
+}
+
+TEST_F(ResolverVariableValidationTest, LetOfPtrConstructedWithRef) {
+    // var a : f32;
+    // let b : ptr<function,f32> = a;
+    const auto priv = ast::StorageClass::kFunction;
+    auto* var_a = Var("a", ty.f32(), priv);
+    auto* var_b = Let(Source{{12, 34}}, "b", ty.pointer<f32>(priv), Expr("a"), {});
+    WrapInFunction(var_a, var_b);
+
+    ASSERT_FALSE(r()->Resolve());
+
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: cannot initialize let of type 'ptr<function, f32, read_write>' with value of type 'f32')");
+}
+
+TEST_F(ResolverVariableValidationTest, LocalLetRedeclared) {
+    // let l : f32 = 1.;
+    // let l : i32 = 0;
+    auto* l1 = Let("l", ty.f32(), Expr(1_f));
+    auto* l2 = Let(Source{{12, 34}}, "l", ty.i32(), Expr(0_i));
+    WrapInFunction(l1, l2);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: redeclaration of 'l'\nnote: 'l' previously declared here");
+}
+
+TEST_F(ResolverVariableValidationTest, GlobalVarRedeclaredAsLocal) {
+    // var v : f32 = 2.1;
+    // fn my_func() {
+    //   var v : f32 = 2.0;
+    //   return 0;
+    // }
+
+    GlobalVar("v", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1_f));
+
+    WrapInFunction(Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone, Expr(2_f)));
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverVariableValidationTest, VarRedeclaredInInnerBlock) {
+    // {
+    //  var v : f32;
+    //  { var v : f32; }
+    // }
+    auto* var_outer = Var("v", ty.f32(), ast::StorageClass::kNone);
+    auto* var_inner = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone);
+    auto* inner = Block(Decl(var_inner));
+    auto* outer_body = Block(Decl(var_outer), inner);
+
+    WrapInFunction(outer_body);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverVariableValidationTest, VarRedeclaredInIfBlock) {
+    // {
+    //   var v : f32 = 3.14;
+    //   if (true) { var v : f32 = 2.0; }
+    // }
+    auto* var_a_float = Var("v", ty.f32(), ast::StorageClass::kNone, Expr(3.1_f));
+
+    auto* var = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone, Expr(2_f));
+
+    auto* cond = Expr(true);
+    auto* body = Block(Decl(var));
+
+    auto* outer_body = Block(Decl(var_a_float), If(cond, body));
+
+    WrapInFunction(outer_body);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverVariableValidationTest, InferredPtrStorageAccessMismatch) {
+    // struct Inner {
+    //    arr: array<i32, 4>;
+    // }
+    // struct S {
+    //    inner: Inner;
+    // }
+    // @group(0) @binding(0) var<storage> s : S;
+    // fn f() {
+    //   let p : pointer<storage, i32, read_write> = &s.inner.arr[2i];
+    // }
+    auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
+    auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+    auto* storage = GlobalVar("s", ty.Of(buf), ast::StorageClass::kStorage,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(0u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
+
+    auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 2_i);
+    auto* ptr =
+        Let(Source{{12, 34}}, "p",
+            ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite), AddressOf(expr));
+
+    WrapInFunction(ptr);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: cannot initialize let of type "
+              "'ptr<storage, i32, read_write>' with value of type "
+              "'ptr<storage, i32, read>'");
+}
+
+TEST_F(ResolverVariableValidationTest, NonConstructibleType_Atomic) {
+    auto* v = Var("v", ty.atomic(Source{{12, 34}}, ty.i32()));
+    WrapInFunction(v);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: function-scope 'var' must have a constructible type");
+}
+
+TEST_F(ResolverVariableValidationTest, NonConstructibleType_RuntimeArray) {
+    auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))});
+    auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
+    WrapInFunction(v);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+56:78 note: while analysing structure member S.m
+12:34 note: while instantiating 'var' v)");
+}
+
+TEST_F(ResolverVariableValidationTest, NonConstructibleType_Struct_WithAtomic) {
+    auto* s = Structure("S", {Member("m", ty.atomic(ty.i32()))});
+    auto* v = Var("v", ty.Of(s));
+    WrapInFunction(v);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "error: function-scope 'var' must have a constructible type");
+}
+
+TEST_F(ResolverVariableValidationTest, NonConstructibleType_InferredType) {
+    // @group(0) @binding(0) var s : sampler;
+    // fn foo() {
+    //   var v = s;
+    // }
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 0));
+    auto* v = Var(Source{{12, 34}}, "v", nullptr, Expr("s"));
+    WrapInFunction(v);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: function-scope 'var' must have a constructible type");
+}
+
+TEST_F(ResolverVariableValidationTest, InvalidStorageClassForInitializer) {
+    // var<workgroup> v : f32 = 1.23;
+    GlobalVar(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kWorkgroup, Expr(1.23_f));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(),
+              "12:34 error: var of storage class 'workgroup' cannot have "
+              "an initializer. var initializers are only supported for the "
+              "storage classes 'private' and 'function'");
+}
+
+TEST_F(ResolverVariableValidationTest, VectorConstNoType) {
+    // const a : mat3x3 = mat3x3<f32>();
+    WrapInFunction(Const("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u), vec3<f32>()));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+}
+
+TEST_F(ResolverVariableValidationTest, VectorLetNoType) {
+    // let a : mat3x3 = mat3x3<f32>();
+    WrapInFunction(Let("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u), vec3<f32>()));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+}
+
+TEST_F(ResolverVariableValidationTest, VectorVarNoType) {
+    // var a : mat3x3;
+    WrapInFunction(Var("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+}
+
+TEST_F(ResolverVariableValidationTest, MatrixConstNoType) {
+    // const a : mat3x3 = mat3x3<f32>();
+    WrapInFunction(
+        Const("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u), mat3x3<f32>()));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+}
+
+TEST_F(ResolverVariableValidationTest, MatrixLetNoType) {
+    // let a : mat3x3 = mat3x3<f32>();
+    WrapInFunction(Let("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u), mat3x3<f32>()));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+}
+
+TEST_F(ResolverVariableValidationTest, MatrixVarNoType) {
+    // var a : mat3x3;
+    WrapInFunction(Var("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3u, 3u)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstStructure) {
+    auto* s = Structure("S", {Member("m", ty.i32())});
+    auto* c = Const("c", ty.Of(s), Construct(Source{{12, 34}}, ty.Of(s)));
+    WrapInFunction(c);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+}
+
+TEST_F(ResolverVariableValidationTest, GlobalConstStructure) {
+    auto* s = Structure("S", {Member("m", ty.i32())});
+    GlobalConst("c", ty.Of(s), Construct(Source{{12, 34}}, ty.Of(s)));
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstInitWithVar) {
+    auto* v = Var("v", nullptr, Expr(1_i));
+    auto* c = Const("c", nullptr, Expr(Source{{12, 34}}, v));
+    WrapInFunction(v, c);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstInitWithOverride) {
+    auto* o = Override("v", nullptr, Expr(1_i));
+    auto* c = Const("c", nullptr, Expr(Source{{12, 34}}, o));
+    WrapInFunction(c);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+}
+
+TEST_F(ResolverVariableValidationTest, ConstInitWithLet) {
+    auto* l = Let("v", nullptr, Expr(1_i));
+    auto* c = Const("c", nullptr, Expr(Source{{12, 34}}, l));
+    WrapInFunction(l, c);
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)");
+}
+
+}  // namespace
+}  // namespace tint::resolver
diff --git a/src/tint/sem/builtin_type.cc b/src/tint/sem/builtin_type.cc
index c95ce19..1888d0b 100644
--- a/src/tint/sem/builtin_type.cc
+++ b/src/tint/sem/builtin_type.cc
@@ -35,6 +35,9 @@
     if (name == "acos") {
         return BuiltinType::kAcos;
     }
+    if (name == "acosh") {
+        return BuiltinType::kAcosh;
+    }
     if (name == "all") {
         return BuiltinType::kAll;
     }
@@ -47,12 +50,18 @@
     if (name == "asin") {
         return BuiltinType::kAsin;
     }
+    if (name == "asinh") {
+        return BuiltinType::kAsinh;
+    }
     if (name == "atan") {
         return BuiltinType::kAtan;
     }
     if (name == "atan2") {
         return BuiltinType::kAtan2;
     }
+    if (name == "atanh") {
+        return BuiltinType::kAtanh;
+    }
     if (name == "ceil") {
         return BuiltinType::kCeil;
     }
@@ -358,6 +367,8 @@
             return "abs";
         case BuiltinType::kAcos:
             return "acos";
+        case BuiltinType::kAcosh:
+            return "acosh";
         case BuiltinType::kAll:
             return "all";
         case BuiltinType::kAny:
@@ -366,10 +377,14 @@
             return "arrayLength";
         case BuiltinType::kAsin:
             return "asin";
+        case BuiltinType::kAsinh:
+            return "asinh";
         case BuiltinType::kAtan:
             return "atan";
         case BuiltinType::kAtan2:
             return "atan2";
+        case BuiltinType::kAtanh:
+            return "atanh";
         case BuiltinType::kCeil:
             return "ceil";
         case BuiltinType::kClamp:
diff --git a/src/tint/sem/builtin_type.h b/src/tint/sem/builtin_type.h
index a119d79..caf7c53 100644
--- a/src/tint/sem/builtin_type.h
+++ b/src/tint/sem/builtin_type.h
@@ -35,12 +35,15 @@
     kNone = -1,
     kAbs,
     kAcos,
+    kAcosh,
     kAll,
     kAny,
     kArrayLength,
     kAsin,
+    kAsinh,
     kAtan,
     kAtan2,
+    kAtanh,
     kCeil,
     kClamp,
     kCos,
diff --git a/src/tint/sem/call.cc b/src/tint/sem/call.cc
index f688cce..bfce3b1 100644
--- a/src/tint/sem/call.cc
+++ b/src/tint/sem/call.cc
@@ -25,9 +25,9 @@
            const CallTarget* target,
            std::vector<const sem::Expression*> arguments,
            const Statement* statement,
-           Constant constant,
+           const Constant* constant,
            bool has_side_effects)
-    : Base(declaration, target->ReturnType(), statement, std::move(constant), has_side_effects),
+    : Base(declaration, target->ReturnType(), statement, constant, has_side_effects),
       target_(target),
       arguments_(std::move(arguments)) {}
 
diff --git a/src/tint/sem/call.h b/src/tint/sem/call.h
index c96179d..1955bf9 100644
--- a/src/tint/sem/call.h
+++ b/src/tint/sem/call.h
@@ -17,6 +17,7 @@
 
 #include <vector>
 
+#include "src/tint/ast/call_expression.h"
 #include "src/tint/sem/builtin.h"
 #include "src/tint/sem/expression.h"
 
@@ -37,7 +38,7 @@
          const CallTarget* target,
          std::vector<const sem::Expression*> arguments,
          const Statement* statement,
-         Constant constant,
+         const Constant* constant,
          bool has_side_effects);
 
     /// Destructor
diff --git a/src/tint/sem/constant.cc b/src/tint/sem/constant.cc
index 8086992..70bb08c 100644
--- a/src/tint/sem/constant.cc
+++ b/src/tint/sem/constant.cc
@@ -14,100 +14,10 @@
 
 #include "src/tint/sem/constant.h"
 
-#include <cmath>
-#include <utility>
-
-#include "src/tint/debug.h"
-#include "src/tint/program_builder.h"
-#include "src/tint/sem/type.h"
-
 namespace tint::sem {
 
-namespace {
-size_t CountElements(const Constant::Elements& elements) {
-    return std::visit([](auto&& vec) { return vec.size(); }, elements);
-}
-
-template <typename T>
-bool IsNegativeFloat(T value) {
-    (void)value;
-    if constexpr (IsFloatingPoint<T>) {
-        return std::signbit(value);
-    } else {
-        return false;
-    }
-}
-
-}  // namespace
-
-Constant::Constant() {}
-
-Constant::Constant(const sem::Type* ty, Elements els)
-    : type_(ty), elem_type_(CheckElemType(ty, CountElements(els))), elems_(std::move(els)) {}
-
-Constant::Constant(const sem::Type* ty, AInts vec) : Constant(ty, Elements{std::move(vec)}) {}
-
-Constant::Constant(const sem::Type* ty, AFloats vec) : Constant(ty, Elements{std::move(vec)}) {}
-
-Constant::Constant(const Constant&) = default;
+Constant::Constant() = default;
 
 Constant::~Constant() = default;
 
-Constant& Constant::operator=(const Constant& rhs) = default;
-
-bool Constant::AnyZero() const {
-    return WithElements([&](auto&& vec) {
-        using T = typename std::decay_t<decltype(vec)>::value_type;
-        for (auto el : vec) {
-            if (el == T(0) && !IsNegativeFloat(el.value)) {
-                return true;
-            }
-        }
-        return false;
-    });
-}
-
-bool Constant::AllZero() const {
-    return WithElements([&](auto&& vec) {
-        using T = typename std::decay_t<decltype(vec)>::value_type;
-        for (auto el : vec) {
-            if (el != T(0) || IsNegativeFloat(el.value)) {
-                return false;
-            }
-        }
-        return true;
-    });
-}
-
-bool Constant::AllEqual(size_t start, size_t end) const {
-    return WithElements([&](auto&& vec) {
-        if (!vec.empty()) {
-            auto value = vec[start];
-            bool float_sign = IsNegativeFloat(vec[start].value);
-            for (size_t i = start + 1; i < end; i++) {
-                if (vec[i] != value || float_sign != IsNegativeFloat(vec[i].value)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    });
-}
-
-const Type* Constant::CheckElemType(const sem::Type* ty, size_t num_elements) {
-    diag::List diag;
-    if (ty->is_abstract_or_scalar() || ty->IsAnyOf<Vector, Matrix>()) {
-        uint32_t count = 0;
-        auto* el_ty = Type::ElementOf(ty, &count);
-        if (num_elements != count) {
-            TINT_ICE(Semantic, diag) << "sem::Constant() type <-> element mismatch. type: '"
-                                     << ty->TypeInfo().name << "' element: " << num_elements;
-        }
-        TINT_ASSERT(Semantic, el_ty->is_abstract_or_scalar());
-        return el_ty;
-    }
-    TINT_UNREACHABLE(Semantic, diag) << "Unsupported sem::Constant type: " << ty->TypeInfo().name;
-    return nullptr;
-}
-
 }  // namespace tint::sem
diff --git a/src/tint/sem/constant.h b/src/tint/sem/constant.h
index 4f7132a..b46127c 100644
--- a/src/tint/sem/constant.h
+++ b/src/tint/sem/constant.h
@@ -15,172 +15,64 @@
 #ifndef SRC_TINT_SEM_CONSTANT_H_
 #define SRC_TINT_SEM_CONSTANT_H_
 
-#include <ostream>
-#include <utility>
 #include <variant>
-#include <vector>
 
-#include "src/tint/program_builder.h"
-#include "src/tint/sem/type.h"
+#include "src/tint/number.h"
+
+// Forward declarations
+namespace tint::sem {
+class Type;
+}
 
 namespace tint::sem {
 
-/// A Constant holds a compile-time evaluated expression value, expressed as a flattened list of
-/// element values. The expression type may be of an abstract-numeric, scalar, vector or matrix
-/// type. Constant holds the element values in either a vector of abstract-integer (AInt) or
-/// abstract-float (AFloat), depending on the element type.
+/// Constant is the interface to a compile-time evaluated expression value.
 class Constant {
   public:
-    /// AInts is a vector of AInt, used to hold elements of the WGSL types:
-    /// * abstract-integer
-    /// * i32
-    /// * u32
-    /// * bool (0 or 1)
-    using AInts = std::vector<AInt>;
-
-    /// AFloats is a vector of AFloat, used to hold elements of the WGSL types:
-    /// * abstract-float
-    /// * f32
-    /// * f16
-    using AFloats = std::vector<AFloat>;
-
-    /// Elements is either a vector of AInts or AFloats
-    using Elements = std::variant<AInts, AFloats>;
-
-    /// Helper that resolves to either AInt or AFloat based on the element type T.
-    template <typename T>
-    using ElementFor = std::conditional_t<IsFloatingPoint<UnwrapNumber<T>>, AFloat, AInt>;
-
-    /// Helper that resolves to either AInts or AFloats based on the element type T.
-    template <typename T>
-    using ElementVectorFor = std::conditional_t<IsFloatingPoint<UnwrapNumber<T>>, AFloats, AInts>;
-
-    /// Constructs an invalid Constant
+    /// Constructor
     Constant();
 
-    /// Constructs a Constant of the given type and element values
-    /// @param ty the Constant type
-    /// @param els the Constant element values
-    Constant(const sem::Type* ty, Elements els);
-
-    /// Constructs a Constant of the given type and element values
-    /// @param ty the Constant type
-    /// @param vec the Constant element values
-    Constant(const sem::Type* ty, AInts vec);
-
-    /// Constructs a Constant of the given type and element values
-    /// @param ty the Constant type
-    /// @param vec the Constant element values
-    Constant(const sem::Type* ty, AFloats vec);
-
-    /// Constructs a Constant of the given type and element values
-    /// @param ty the Constant type
-    /// @param els the Constant element values
-    template <typename T>
-    Constant(const sem::Type* ty, std::initializer_list<T> els);
-
-    /// Copy constructor
-    Constant(const Constant&);
-
     /// Destructor
-    ~Constant();
+    virtual ~Constant();
 
-    /// Copy assignment
-    /// @param other the Constant to copy
-    /// @returns this Constant
-    Constant& operator=(const Constant& other);
+    /// @returns the type of the constant
+    virtual const sem::Type* Type() const = 0;
 
-    /// @returns true if the Constant has been initialized
-    bool IsValid() const { return type_ != nullptr; }
+    /// @returns the value of this Constant, if this constant is of a scalar value or abstract
+    /// numeric, otherwsie std::monostate.
+    virtual std::variant<std::monostate, AInt, AFloat> Value() const = 0;
 
-    /// @return true if the Constant has been initialized
-    operator bool() const { return IsValid(); }
+    /// @returns the child constant element with the given index, or nullptr if the constant has no
+    /// children, or the index is out of bounds.
+    virtual const Constant* Index(size_t) const = 0;
 
-    /// @returns the type of the Constant
-    const sem::Type* Type() const { return type_; }
+    /// @returns true if child elements of this constant are positive-zero valued.
+    virtual bool AllZero() const = 0;
 
-    /// @returns the number of elements
-    size_t ElementCount() const {
-        return std::visit([](auto&& v) { return v.size(); }, elems_);
-    }
+    /// @returns true if any child elements of this constant are positive-zero valued.
+    virtual bool AnyZero() const = 0;
 
-    /// @returns the element type of the Constant
-    const sem::Type* ElementType() const { return elem_type_; }
+    /// @returns true if all child elements of this constant have the same value and type.
+    virtual bool AllEqual() const = 0;
 
-    /// @returns the constant's elements
-    const Elements& GetElements() const { return elems_; }
+    /// @returns a hash of the constant.
+    virtual size_t Hash() const = 0;
 
-    /// WithElements calls the function `f` with the vector of elements as either AFloats or AInts
-    /// @param f a function-like with the signature `R(auto&&)`.
-    /// @returns the result of calling `f`.
-    template <typename F>
-    auto WithElements(F&& f) const {
-        return std::visit(std::forward<F>(f), elems_);
-    }
-
-    /// WithElements calls the function `f` with the element vector as either AFloats or AInts
-    /// @param f a function-like with the signature `R(auto&&)`.
-    /// @returns the result of calling `f`.
-    template <typename F>
-    auto WithElements(F&& f) {
-        return std::visit(std::forward<F>(f), elems_);
-    }
-
-    /// @returns the elements as a vector of AInt
-    inline const AInts& IElements() const { return std::get<AInts>(elems_); }
-
-    /// @returns the elements as a vector of AFloat
-    inline const AFloats& FElements() const { return std::get<AFloats>(elems_); }
-
-    /// @returns true if any element is positive zero
-    bool AnyZero() const;
-
-    /// @returns true if all elements are positive zero
-    bool AllZero() const;
-
-    /// @returns true if all elements are the same value, with the same sign-bit.
-    bool AllEqual() const { return AllEqual(0, ElementCount()); }
-
-    /// @param start the first element index
-    /// @param end one past the last element index
-    /// @returns true if all elements between `[start, end)` are the same value
-    bool AllEqual(size_t start, size_t end) const;
-
-    /// @param index the index of the element
-    /// @return the element at `index`, which must be of type `T`.
+    /// @returns the value of the constant as the given scalar or abstract value.
     template <typename T>
-    T Element(size_t index) const;
-
-  private:
-    /// Checks that the provided type matches the number of expected elements.
-    /// @returns the element type of `ty`.
-    const sem::Type* CheckElemType(const sem::Type* ty, size_t num_elements);
-
-    const sem::Type* type_ = nullptr;
-    const sem::Type* elem_type_ = nullptr;
-    Elements elems_;
+    T As() const {
+        return std::visit(
+            [](auto v) {
+                if constexpr (std::is_same_v<decltype(v), std::monostate>) {
+                    return T(0);
+                } else {
+                    return static_cast<T>(v);
+                }
+            },
+            Value());
+    }
 };
 
-template <typename T>
-Constant::Constant(const sem::Type* ty, std::initializer_list<T> els)
-    : type_(ty), elem_type_(CheckElemType(type_, els.size())) {
-    ElementVectorFor<T> elements;
-    elements.reserve(els.size());
-    for (auto el : els) {
-        elements.emplace_back(ElementFor<T>(el));
-    }
-    elems_ = Elements{std::move(elements)};
-}
-
-template <typename T>
-T Constant::Element(size_t index) const {
-    if constexpr (std::is_same_v<ElementVectorFor<T>, AFloats>) {
-        return static_cast<T>(FElements()[index].value);
-    } else {
-        return static_cast<T>(IElements()[index].value);
-    }
-}
-
 }  // namespace tint::sem
 
 #endif  // SRC_TINT_SEM_CONSTANT_H_
diff --git a/src/tint/sem/constant_test.cc b/src/tint/sem/constant_test.cc
deleted file mode 100644
index ed9fef8..0000000
--- a/src/tint/sem/constant_test.cc
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2022 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/sem/constant.h"
-
-#include <gmock/gmock.h>
-
-#include "src/tint/sem/abstract_float.h"
-#include "src/tint/sem/abstract_int.h"
-#include "src/tint/sem/test_helper.h"
-
-using namespace tint::number_suffixes;  // NOLINT
-
-namespace tint::sem {
-namespace {
-
-using ConstantTest = TestHelper;
-
-TEST_F(ConstantTest, ConstructorInitializerList) {
-    {
-        auto i = AInt(AInt::kHighest);
-        Constant c(create<AbstractInt>(), {i});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(i)); });
-    }
-    {
-        auto i = i32(i32::kHighest);
-        Constant c(create<I32>(), {i});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(i)); });
-    }
-    {
-        auto i = u32(u32::kHighest);
-        Constant c(create<U32>(), {i});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(i)); });
-    }
-    {
-        Constant c(create<Bool>(), {false});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(0_a)); });
-    }
-    {
-        Constant c(create<Bool>(), {true});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(1_a)); });
-    }
-    {
-        auto f = AFloat(AFloat::kHighest);
-        Constant c(create<AbstractFloat>(), {f});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(f)); });
-    }
-    {
-        auto f = f32(f32::kHighest);
-        Constant c(create<F32>(), {f});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(f)); });
-    }
-    {
-        auto f = f16(f16::kHighest);
-        Constant c(create<F16>(), {f});
-        c.WithElements([&](auto&& vec) { EXPECT_THAT(vec, testing::ElementsAre(f)); });
-    }
-}
-
-TEST_F(ConstantTest, Element_ai) {
-    Constant c(create<AbstractInt>(), {1_a});
-    EXPECT_EQ(c.Element<AInt>(0), 1_a);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_i32) {
-    Constant c(create<I32>(), {1_a});
-    EXPECT_EQ(c.Element<i32>(0), 1_i);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_u32) {
-    Constant c(create<U32>(), {1_a});
-    EXPECT_EQ(c.Element<u32>(0), 1_u);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_bool) {
-    Constant c(create<Bool>(), {true});
-    EXPECT_EQ(c.Element<bool>(0), true);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_af) {
-    Constant c(create<AbstractFloat>(), {1.0_a});
-    EXPECT_EQ(c.Element<AFloat>(0), 1.0_a);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_f32) {
-    Constant c(create<F32>(), {1.0_a});
-    EXPECT_EQ(c.Element<f32>(0), 1.0_f);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_f16) {
-    Constant c(create<F16>(), {1.0_a});
-    EXPECT_EQ(c.Element<f16>(0), 1.0_h);
-    EXPECT_EQ(c.ElementCount(), 1u);
-}
-
-TEST_F(ConstantTest, Element_vec3_ai) {
-    Constant c(create<Vector>(create<AbstractInt>(), 3u), {1_a, 2_a, 3_a});
-    EXPECT_EQ(c.Element<AInt>(0), 1_a);
-    EXPECT_EQ(c.Element<AInt>(1), 2_a);
-    EXPECT_EQ(c.Element<AInt>(2), 3_a);
-    EXPECT_EQ(c.ElementCount(), 3u);
-}
-
-TEST_F(ConstantTest, Element_vec3_i32) {
-    Constant c(create<Vector>(create<I32>(), 3u), {1_a, 2_a, 3_a});
-    EXPECT_EQ(c.Element<i32>(0), 1_i);
-    EXPECT_EQ(c.Element<i32>(1), 2_i);
-    EXPECT_EQ(c.Element<i32>(2), 3_i);
-    EXPECT_EQ(c.ElementCount(), 3u);
-}
-
-TEST_F(ConstantTest, Element_vec3_u32) {
-    Constant c(create<Vector>(create<U32>(), 3u), {1_a, 2_a, 3_a});
-    EXPECT_EQ(c.Element<u32>(0), 1_u);
-    EXPECT_EQ(c.Element<u32>(1), 2_u);
-    EXPECT_EQ(c.Element<u32>(2), 3_u);
-    EXPECT_EQ(c.ElementCount(), 3u);
-}
-
-TEST_F(ConstantTest, Element_vec3_bool) {
-    Constant c(create<Vector>(create<Bool>(), 2u), {true, false});
-    EXPECT_EQ(c.Element<bool>(0), true);
-    EXPECT_EQ(c.Element<bool>(1), false);
-    EXPECT_EQ(c.ElementCount(), 2u);
-}
-
-TEST_F(ConstantTest, Element_vec3_af) {
-    Constant c(create<Vector>(create<AbstractFloat>(), 3u), {1.0_a, 2.0_a, 3.0_a});
-    EXPECT_EQ(c.Element<AFloat>(0), 1.0_a);
-    EXPECT_EQ(c.Element<AFloat>(1), 2.0_a);
-    EXPECT_EQ(c.Element<AFloat>(2), 3.0_a);
-    EXPECT_EQ(c.ElementCount(), 3u);
-}
-
-TEST_F(ConstantTest, Element_vec3_f32) {
-    Constant c(create<Vector>(create<F32>(), 3u), {1.0_a, 2.0_a, 3.0_a});
-    EXPECT_EQ(c.Element<f32>(0), 1.0_f);
-    EXPECT_EQ(c.Element<f32>(1), 2.0_f);
-    EXPECT_EQ(c.Element<f32>(2), 3.0_f);
-    EXPECT_EQ(c.ElementCount(), 3u);
-}
-
-TEST_F(ConstantTest, Element_vec3_f16) {
-    Constant c(create<Vector>(create<F16>(), 3u), {1.0_a, 2.0_a, 3.0_a});
-    EXPECT_EQ(c.Element<f16>(0), 1.0_h);
-    EXPECT_EQ(c.Element<f16>(1), 2.0_h);
-    EXPECT_EQ(c.Element<f16>(2), 3.0_h);
-    EXPECT_EQ(c.ElementCount(), 3u);
-}
-
-TEST_F(ConstantTest, Element_mat2x3_af) {
-    Constant c(create<Matrix>(create<Vector>(create<AbstractFloat>(), 3u), 2u),
-               {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a});
-    EXPECT_EQ(c.Element<AFloat>(0), 1.0_a);
-    EXPECT_EQ(c.Element<AFloat>(1), 2.0_a);
-    EXPECT_EQ(c.Element<AFloat>(2), 3.0_a);
-    EXPECT_EQ(c.Element<AFloat>(3), 4.0_a);
-    EXPECT_EQ(c.Element<AFloat>(4), 5.0_a);
-    EXPECT_EQ(c.Element<AFloat>(5), 6.0_a);
-    EXPECT_EQ(c.ElementCount(), 6u);
-}
-
-TEST_F(ConstantTest, Element_mat2x3_f32) {
-    Constant c(create<Matrix>(create<Vector>(create<F32>(), 3u), 2u),
-               {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a});
-    EXPECT_EQ(c.Element<f32>(0), 1.0_f);
-    EXPECT_EQ(c.Element<f32>(1), 2.0_f);
-    EXPECT_EQ(c.Element<f32>(2), 3.0_f);
-    EXPECT_EQ(c.Element<f32>(3), 4.0_f);
-    EXPECT_EQ(c.Element<f32>(4), 5.0_f);
-    EXPECT_EQ(c.Element<f32>(5), 6.0_f);
-    EXPECT_EQ(c.ElementCount(), 6u);
-}
-
-TEST_F(ConstantTest, Element_mat2x3_f16) {
-    Constant c(create<Matrix>(create<Vector>(create<F16>(), 3u), 2u),
-               {1.0_a, 2.0_a, 3.0_a, 4.0_a, 5.0_a, 6.0_a});
-    EXPECT_EQ(c.Element<f16>(0), 1.0_h);
-    EXPECT_EQ(c.Element<f16>(1), 2.0_h);
-    EXPECT_EQ(c.Element<f16>(2), 3.0_h);
-    EXPECT_EQ(c.Element<f16>(3), 4.0_h);
-    EXPECT_EQ(c.Element<f16>(4), 5.0_h);
-    EXPECT_EQ(c.Element<f16>(5), 6.0_h);
-    EXPECT_EQ(c.ElementCount(), 6u);
-}
-
-TEST_F(ConstantTest, AnyZero) {
-    auto* vec3_ai = create<Vector>(create<AbstractInt>(), 3u);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AnyZero(), false);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 3_a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 3_a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 0_a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AnyZero(), true);
-
-    auto* vec3_af = create<Vector>(create<AbstractFloat>(), 3u);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AnyZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 3._a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 3._a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 0._a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AnyZero(), true);
-
-    EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 3._a}).AnyZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -2._a, 3._a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 3._a}).AnyZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 0._a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AnyZero(), true);
-    EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, -0._a}).AnyZero(), false);
-}
-
-TEST_F(ConstantTest, AllZero) {
-    auto* vec3_ai = create<Vector>(create<AbstractInt>(), 3u);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 2_a, 3_a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 3_a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 0_a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AllZero(), true);
-
-    auto* vec3_af = create<Vector>(create<AbstractFloat>(), 3u);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 2._a, 3._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 3._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 0._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AllZero(), true);
-
-    EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 3._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -2._a, 3._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 3._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, -2._a, 0._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AllZero(), false);
-    EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, -0._a}).AllZero(), false);
-}
-
-TEST_F(ConstantTest, AllEqual) {
-    auto* vec3_ai = create<Vector>(create<AbstractInt>(), 3u);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllEqual(), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 3_a}).AllEqual(), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 3_a, 3_a}).AllEqual(), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 1_a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_ai, {2_a, 2_a, 2_a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_ai, {3_a, 3_a, 3_a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AllEqual(), true);
-
-    auto* vec3_af = create<Vector>(create<AbstractFloat>(), 3u);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllEqual(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 3._a}).AllEqual(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 3._a, 3._a}).AllEqual(), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 1._a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_af, {2._a, 2._a, 2._a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_af, {3._a, 3._a, 3._a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AllEqual(), true);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AllEqual(), false);
-}
-
-TEST_F(ConstantTest, AllEqualRange) {
-    auto* vec3_ai = create<Vector>(create<AbstractInt>(), 3u);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 2_a, 3_a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 3_a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 3_a, 3_a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 1_a, 1_a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_ai, {2_a, 2_a, 2_a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_ai, {2_a, 2_a, 3_a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_ai, {1_a, 0_a, 0_a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 1_a, 0_a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 1_a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_ai, {0_a, 0_a, 0_a}).AllEqual(1, 3), true);
-
-    auto* vec3_af = create<Vector>(create<AbstractFloat>(), 3u);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 2._a, 3._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 3._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 3._a, 3._a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 1._a, 1._a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_af, {2._a, 2._a, 2._a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_af, {2._a, 2._a, 3._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {1._a, 0._a, 0._a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 1._a, 0._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 1._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, 0._a, 0._a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_af, {1._a, -0._a, 0._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -1._a, 0._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 1._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, 0._a}).AllEqual(1, 3), false);
-    EXPECT_EQ(Constant(vec3_af, {0._a, -0._a, -0._a}).AllEqual(1, 3), true);
-    EXPECT_EQ(Constant(vec3_af, {-0._a, -0._a, -0._a}).AllEqual(1, 3), true);
-}
-
-}  // namespace
-}  // namespace tint::sem
diff --git a/src/tint/sem/expression.cc b/src/tint/sem/expression.cc
index 57ec68b..4415db5 100644
--- a/src/tint/sem/expression.cc
+++ b/src/tint/sem/expression.cc
@@ -25,7 +25,7 @@
 Expression::Expression(const ast::Expression* declaration,
                        const sem::Type* type,
                        const Statement* statement,
-                       Constant constant,
+                       const Constant* constant,
                        bool has_side_effects,
                        const Variable* source_var /* = nullptr */)
     : declaration_(declaration),
diff --git a/src/tint/sem/expression.h b/src/tint/sem/expression.h
index a783851..05e5dac 100644
--- a/src/tint/sem/expression.h
+++ b/src/tint/sem/expression.h
@@ -41,7 +41,7 @@
     Expression(const ast::Expression* declaration,
                const sem::Type* type,
                const Statement* statement,
-               Constant constant,
+               const Constant* constant,
                bool has_side_effects,
                const Variable* source_var = nullptr);
 
@@ -58,7 +58,7 @@
     const Statement* Stmt() const { return statement_; }
 
     /// @return the constant value of this expression
-    const Constant& ConstantValue() const { return constant_; }
+    const Constant* ConstantValue() const { return constant_; }
 
     /// Returns the variable or parameter that this expression derives from.
     /// For reference and pointer expressions, this will either be the originating
@@ -88,7 +88,7 @@
   private:
     const sem::Type* const type_;
     const Statement* const statement_;
-    const Constant constant_;
+    const Constant* const constant_;
     sem::Behaviors behaviors_{sem::Behavior::kNext};
     const bool has_side_effects_;
 };
diff --git a/src/tint/sem/expression_test.cc b/src/tint/sem/expression_test.cc
index fc1adeb..cc4bf0e 100644
--- a/src/tint/sem/expression_test.cc
+++ b/src/tint/sem/expression_test.cc
@@ -23,13 +23,30 @@
 namespace tint::sem {
 namespace {
 
+class MockConstant : public sem::Constant {
+  public:
+    explicit MockConstant(const sem::Type* ty) : type(ty) {}
+    ~MockConstant() override {}
+    const sem::Type* Type() const override { return type; }
+    std::variant<std::monostate, AInt, AFloat> Value() const override { return {}; }
+    const Constant* Index(size_t) const override { return {}; }
+    bool AllZero() const override { return {}; }
+    bool AnyZero() const override { return {}; }
+    bool AllEqual() const override { return {}; }
+    size_t Hash() const override { return 0; }
+
+  private:
+    const sem::Type* type;
+};
+
 using ExpressionTest = TestHelper;
 
 TEST_F(ExpressionTest, UnwrapMaterialize) {
+    MockConstant c(create<I32>());
     auto* a = create<Expression>(/* declaration */ nullptr, create<I32>(), /* statement */ nullptr,
-                                 Constant{},
+                                 /* constant_value */ nullptr,
                                  /* has_side_effects */ false, /* source_var */ nullptr);
-    auto* b = create<Materialize>(a, /* statement */ nullptr, Constant{create<I32>(), {1_a}});
+    auto* b = create<Materialize>(a, /* statement */ nullptr, &c);
 
     EXPECT_EQ(a, a->UnwrapMaterialize());
     EXPECT_EQ(a, b->UnwrapMaterialize());
diff --git a/src/tint/sem/index_accessor_expression.cc b/src/tint/sem/index_accessor_expression.cc
new file mode 100644
index 0000000..cd74201
--- /dev/null
+++ b/src/tint/sem/index_accessor_expression.cc
@@ -0,0 +1,39 @@
+// Copyright 2021 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/sem/index_accessor_expression.h"
+
+#include "src/tint/ast/index_accessor_expression.h"
+
+#include <utility>
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::IndexAccessorExpression);
+
+namespace tint::sem {
+
+IndexAccessorExpression::IndexAccessorExpression(const ast::IndexAccessorExpression* declaration,
+                                                 const sem::Type* type,
+                                                 const Expression* object,
+                                                 const Expression* index,
+                                                 const Statement* statement,
+                                                 const Constant* constant,
+                                                 bool has_side_effects,
+                                                 const Variable* source_var /* = nullptr */)
+    : Base(declaration, type, statement, constant, has_side_effects, source_var),
+      object_(object),
+      index_(index) {}
+
+IndexAccessorExpression::~IndexAccessorExpression() = default;
+
+}  // namespace tint::sem
diff --git a/src/tint/sem/index_accessor_expression.h b/src/tint/sem/index_accessor_expression.h
new file mode 100644
index 0000000..233b0fa
--- /dev/null
+++ b/src/tint/sem/index_accessor_expression.h
@@ -0,0 +1,66 @@
+// Copyright 2022 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_SEM_INDEX_ACCESSOR_EXPRESSION_H_
+#define SRC_TINT_SEM_INDEX_ACCESSOR_EXPRESSION_H_
+
+#include <vector>
+
+#include "src/tint/sem/expression.h"
+
+// Forward declarations
+namespace tint::ast {
+class IndexAccessorExpression;
+}  // namespace tint::ast
+
+namespace tint::sem {
+
+/// IndexAccessorExpression holds the semantic information for a ast::IndexAccessorExpression node.
+class IndexAccessorExpression final : public Castable<IndexAccessorExpression, Expression> {
+  public:
+    /// Constructor
+    /// @param declaration the AST node
+    /// @param type the resolved type of the expression
+    /// @param object the object expression that is being indexed
+    /// @param index the index expression
+    /// @param statement the statement that owns this expression
+    /// @param constant the constant value of the expression. May be invalid
+    /// @param has_side_effects whether this expression may have side effects
+    /// @param source_var the (optional) source variable for this expression
+    IndexAccessorExpression(const ast::IndexAccessorExpression* declaration,
+                            const sem::Type* type,
+                            const Expression* object,
+                            const Expression* index,
+                            const Statement* statement,
+                            const Constant* constant,
+                            bool has_side_effects,
+                            const Variable* source_var = nullptr);
+
+    /// Destructor
+    ~IndexAccessorExpression() override;
+
+    /// @returns the object expression that is being indexed
+    Expression const* Object() const { return object_; }
+
+    /// @returns the index expression
+    Expression const* Index() const { return index_; }
+
+  private:
+    Expression const* const object_;
+    Expression const* const index_;
+};
+
+}  // namespace tint::sem
+
+#endif  // SRC_TINT_SEM_INDEX_ACCESSOR_EXPRESSION_H_
diff --git a/src/tint/sem/materialize.cc b/src/tint/sem/materialize.cc
index 76dd9d4..739b0ce 100644
--- a/src/tint/sem/materialize.cc
+++ b/src/tint/sem/materialize.cc
@@ -17,19 +17,16 @@
 TINT_INSTANTIATE_TYPEINFO(tint::sem::Materialize);
 
 namespace tint::sem {
-
-Materialize::Materialize(const Expression* expr, const Statement* statement, Constant constant)
+Materialize::Materialize(const Expression* expr,
+                         const Statement* statement,
+                         const Constant* constant)
     : Base(/* declaration */ expr->Declaration(),
-           /* type */ constant.Type(),
+           /* type */ constant->Type(),
            /* statement */ statement,
            /* constant */ constant,
            /* has_side_effects */ false,
            /* source_var */ expr->SourceVariable()),
-      expr_(expr) {
-    // Materialize nodes only wrap compile-time expressions, and so the Materialize expression must
-    // have a constant value.
-    TINT_ASSERT(Semantic, constant.IsValid());
-}
+      expr_(expr) {}
 
 Materialize::~Materialize() = default;
 
diff --git a/src/tint/sem/materialize.h b/src/tint/sem/materialize.h
index a7c0e3a..0cbac29 100644
--- a/src/tint/sem/materialize.h
+++ b/src/tint/sem/materialize.h
@@ -31,7 +31,7 @@
     /// @param expr the inner expression, being materialized
     /// @param statement the statement that owns this expression
     /// @param constant the constant value of this expression
-    Materialize(const Expression* expr, const Statement* statement, Constant constant);
+    Materialize(const Expression* expr, const Statement* statement, const Constant* constant);
 
     /// Destructor
     ~Materialize() override;
diff --git a/src/tint/sem/member_accessor_expression.cc b/src/tint/sem/member_accessor_expression.cc
index 9dcca76..f9929c7 100644
--- a/src/tint/sem/member_accessor_expression.cc
+++ b/src/tint/sem/member_accessor_expression.cc
@@ -26,29 +26,32 @@
 MemberAccessorExpression::MemberAccessorExpression(const ast::MemberAccessorExpression* declaration,
                                                    const sem::Type* type,
                                                    const Statement* statement,
+                                                   const Expression* object,
                                                    bool has_side_effects,
                                                    const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, statement, Constant{}, has_side_effects, source_var) {}
+    : Base(declaration, type, statement, nullptr, has_side_effects, source_var), object_(object) {}
 
 MemberAccessorExpression::~MemberAccessorExpression() = default;
 
 StructMemberAccess::StructMemberAccess(const ast::MemberAccessorExpression* declaration,
                                        const sem::Type* type,
                                        const Statement* statement,
+                                       const Expression* object,
                                        const StructMember* member,
                                        bool has_side_effects,
                                        const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, statement, has_side_effects, source_var), member_(member) {}
+    : Base(declaration, type, statement, object, has_side_effects, source_var), member_(member) {}
 
 StructMemberAccess::~StructMemberAccess() = default;
 
 Swizzle::Swizzle(const ast::MemberAccessorExpression* declaration,
                  const sem::Type* type,
                  const Statement* statement,
+                 const Expression* object,
                  std::vector<uint32_t> indices,
                  bool has_side_effects,
                  const Variable* source_var /* = nullptr */)
-    : Base(declaration, type, statement, has_side_effects, source_var),
+    : Base(declaration, type, statement, object, has_side_effects, source_var),
       indices_(std::move(indices)) {}
 
 Swizzle::~Swizzle() = default;
diff --git a/src/tint/sem/member_accessor_expression.h b/src/tint/sem/member_accessor_expression.h
index 0233541..3d60816 100644
--- a/src/tint/sem/member_accessor_expression.h
+++ b/src/tint/sem/member_accessor_expression.h
@@ -38,16 +38,24 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
+    /// @param object the object that holds the member being accessed
     /// @param has_side_effects whether this expression may have side effects
     /// @param source_var the (optional) source variable for this expression
     MemberAccessorExpression(const ast::MemberAccessorExpression* declaration,
                              const sem::Type* type,
                              const Statement* statement,
+                             const Expression* object,
                              bool has_side_effects,
                              const Variable* source_var = nullptr);
 
     /// Destructor
     ~MemberAccessorExpression() override;
+
+    /// @returns the object that holds the member being accessed
+    const Expression* Object() const { return object_; }
+
+  private:
+    Expression const* const object_;
 };
 
 /// StructMemberAccess holds the semantic information for a
@@ -59,12 +67,14 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
+    /// @param object the object that holds the member being accessed
     /// @param member the structure member
     /// @param has_side_effects whether this expression may have side effects
     /// @param source_var the (optional) source variable for this expression
     StructMemberAccess(const ast::MemberAccessorExpression* declaration,
                        const sem::Type* type,
                        const Statement* statement,
+                       const Expression* object,
                        const StructMember* member,
                        bool has_side_effects,
                        const Variable* source_var = nullptr);
@@ -87,12 +97,14 @@
     /// @param declaration the AST node
     /// @param type the resolved type of the expression
     /// @param statement the statement that owns this expression
+    /// @param object the object that holds the member being accessed
     /// @param indices the swizzle indices
     /// @param has_side_effects whether this expression may have side effects
     /// @param source_var the (optional) source variable for this expression
     Swizzle(const ast::MemberAccessorExpression* declaration,
             const sem::Type* type,
             const Statement* statement,
+            const Expression* object,
             std::vector<uint32_t> indices,
             bool has_side_effects,
             const Variable* source_var = nullptr);
diff --git a/src/tint/sem/struct.h b/src/tint/sem/struct.h
index fe9169d..e026b11 100644
--- a/src/tint/sem/struct.h
+++ b/src/tint/sem/struct.h
@@ -194,9 +194,16 @@
     /// @returns the AST declaration node
     const ast::StructMember* Declaration() const { return declaration_; }
 
-    /// @returns the name of the structure
+    /// @returns the name of the structure member
     Symbol Name() const { return name_; }
 
+    /// Sets the owning structure to `s`
+    /// @param s the new structure owner
+    void SetStruct(const sem::Struct* s) { struct_ = s; }
+
+    /// @returns the structure that owns this member
+    const sem::Struct* Struct() const { return struct_; }
+
     /// @returns the type of the member
     sem::Type* Type() const { return type_; }
 
@@ -215,6 +222,7 @@
   private:
     const ast::StructMember* const declaration_;
     const Symbol name_;
+    const sem::Struct* struct_;
     sem::Type* const type_;
     const uint32_t index_;
     const uint32_t offset_;
diff --git a/src/tint/sem/type.cc b/src/tint/sem/type.cc
index 40666e3..9d4c469 100644
--- a/src/tint/sem/type.cc
+++ b/src/tint/sem/type.cc
@@ -220,18 +220,38 @@
         },
         [&](const Matrix* m) {
             if (count) {
-                *count = m->columns() * m->rows();
+                *count = m->columns();
             }
-            return m->type();
+            return m->ColumnType();
         },
         [&](const Array* a) {
             if (count) {
                 *count = a->Count();
             }
             return a->ElemType();
+        },
+        [&](Default) {
+            if (count) {
+                *count = 0;
+            }
+            return nullptr;
         });
 }
 
+const Type* Type::DeepestElementOf(const Type* ty, uint32_t* count /* = nullptr */) {
+    auto el_ty = ElementOf(ty, count);
+    while (el_ty && ty != el_ty) {
+        ty = el_ty;
+
+        uint32_t n = 0;
+        el_ty = ElementOf(ty, &n);
+        if (count) {
+            *count *= n;
+        }
+    }
+    return el_ty;
+}
+
 const sem::Type* Type::Common(Type const* const* types, size_t count) {
     if (count == 0) {
         return nullptr;
diff --git a/src/tint/sem/type.h b/src/tint/sem/type.h
index 9987637..25f3a43 100644
--- a/src/tint/sem/type.h
+++ b/src/tint/sem/type.h
@@ -130,11 +130,26 @@
     static uint32_t ConversionRank(const Type* from, const Type* to);
 
     /// @param ty the type to obtain the element type from
-    /// @param count if not null, then this is assigned the number of elements in the type
-    /// @returns `ty` if `ty` is an abstract or scalar, the element type if ty is a vector, matrix
-    /// or array, otherwise nullptr.
+    /// @param count if not null, then this is assigned the number of child elements in the type.
+    /// For example, the count of an `array<vec3<f32>, 5>` type would be 5.
+    /// @returns
+    ///   * `ty` if `ty` is an abstract or scalar
+    ///   * the element type if `ty` is a vector or array
+    ///   * the column type if `ty` is a matrix
+    ///   * `nullptr` if `ty` is none of the above
     static const Type* ElementOf(const Type* ty, uint32_t* count = nullptr);
 
+    /// @param ty the type to obtain the deepest element type from
+    /// @param count if not null, then this is assigned the full number of most deeply nested
+    /// elements in the type. For example, the count of an `array<vec3<f32>, 5>` type would be 15.
+    /// @returns
+    ///   * `ty` if `ty` is an abstract or scalar
+    ///   * the element type if `ty` is a vector
+    ///   * the matrix element type if `ty` is a matrix
+    ///   * the deepest element type if `ty` is an array
+    ///   * `nullptr` if `ty` is none of the above
+    static const Type* DeepestElementOf(const Type* ty, uint32_t* count = nullptr);
+
     /// @param types a pointer to a list of `const Type*`.
     /// @param count the number of types in `types`.
     /// @returns the lowest-ranking type that all types in `types` can be implicitly converted to,
diff --git a/src/tint/sem/type_mappings.h b/src/tint/sem/type_mappings.h
index 9041bdb..7dc0ade 100644
--- a/src/tint/sem/type_mappings.h
+++ b/src/tint/sem/type_mappings.h
@@ -25,6 +25,7 @@
 class ForLoopStatement;
 class Function;
 class IfStatement;
+class IndexAccessorExpression;
 class MemberAccessorExpression;
 class Node;
 class Override;
@@ -44,6 +45,7 @@
 class ForLoopStatement;
 class Function;
 class IfStatement;
+class IndexAccessorExpression;
 class MemberAccessorExpression;
 class Node;
 class GlobalVariable;
@@ -69,6 +71,7 @@
     ForLoopStatement* operator()(ast::ForLoopStatement*);
     Function* operator()(ast::Function*);
     IfStatement* operator()(ast::IfStatement*);
+    IndexAccessorExpression* operator()(ast::IndexAccessorExpression*);
     MemberAccessorExpression* operator()(ast::MemberAccessorExpression*);
     Node* operator()(ast::Node*);
     GlobalVariable* operator()(ast::Override*);
diff --git a/src/tint/sem/type_test.cc b/src/tint/sem/type_test.cc
index c11efea..8458880 100644
--- a/src/tint/sem/type_test.cc
+++ b/src/tint/sem/type_test.cc
@@ -104,6 +104,20 @@
     auto* mat2x4_f32 = create<Matrix>(vec4_f32, 2u);
     auto* mat4x2_f32 = create<Matrix>(vec2_f32, 4u);
     auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+    auto* str = create<Struct>(nullptr, Sym("s"),
+                               StructMemberList{
+                                   create<StructMember>(
+                                       /* declaration */ nullptr,
+                                       /* name */ Sym("x"),
+                                       /* type */ f16,
+                                       /* index */ 0u,
+                                       /* offset */ 0u,
+                                       /* align */ 4u,
+                                       /* size */ 4u),
+                               },
+                               /* align*/ 4u,
+                               /* size*/ 4u,
+                               /* size_no_padding*/ 4u);
     auto* arr_i32 = create<Array>(
         /* element */ i32,
         /* count */ 5u,
@@ -111,6 +125,27 @@
         /* size */ 5u * 4u,
         /* stride */ 5u * 4u,
         /* implicit_stride */ 5u * 4u);
+    auto* arr_vec3_i32 = create<Array>(
+        /* element */ vec3_i32,
+        /* count */ 5u,
+        /* align */ 16u,
+        /* size */ 5u * 16u,
+        /* stride */ 5u * 16u,
+        /* implicit_stride */ 5u * 16u);
+    auto* arr_mat4x3_f16 = create<Array>(
+        /* element */ mat4x3_f16,
+        /* count */ 5u,
+        /* align */ 64u,
+        /* size */ 5u * 64u,
+        /* stride */ 5u * 64u,
+        /* implicit_stride */ 5u * 64u);
+    auto* arr_str = create<Array>(
+        /* element */ str,
+        /* count */ 5u,
+        /* align */ 4u,
+        /* size */ 5u * 4u,
+        /* stride */ 5u * 4u,
+        /* implicit_stride */ 5u * 4u);
 
     // No count
     EXPECT_TYPE(Type::ElementOf(f32), f32);
@@ -122,51 +157,196 @@
     EXPECT_TYPE(Type::ElementOf(vec4_f32), f32);
     EXPECT_TYPE(Type::ElementOf(vec3_u32), u32);
     EXPECT_TYPE(Type::ElementOf(vec3_i32), i32);
-    EXPECT_TYPE(Type::ElementOf(mat2x4_f32), f32);
-    EXPECT_TYPE(Type::ElementOf(mat4x2_f32), f32);
-    EXPECT_TYPE(Type::ElementOf(mat4x3_f16), f16);
+    EXPECT_TYPE(Type::ElementOf(mat2x4_f32), vec4_f32);
+    EXPECT_TYPE(Type::ElementOf(mat4x2_f32), vec2_f32);
+    EXPECT_TYPE(Type::ElementOf(mat4x3_f16), vec3_f16);
+    EXPECT_TYPE(Type::ElementOf(str), nullptr);
     EXPECT_TYPE(Type::ElementOf(arr_i32), i32);
+    EXPECT_TYPE(Type::ElementOf(arr_vec3_i32), vec3_i32);
+    EXPECT_TYPE(Type::ElementOf(arr_mat4x3_f16), mat4x3_f16);
+    EXPECT_TYPE(Type::ElementOf(arr_str), str);
 
     // With count
-    uint32_t count = 0;
+    uint32_t count = 42;
     EXPECT_TYPE(Type::ElementOf(f32, &count), f32);
     EXPECT_EQ(count, 1u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(f16, &count), f16);
     EXPECT_EQ(count, 1u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(i32, &count), i32);
     EXPECT_EQ(count, 1u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(u32, &count), u32);
     EXPECT_EQ(count, 1u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(vec2_f32, &count), f32);
     EXPECT_EQ(count, 2u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(vec3_f16, &count), f16);
     EXPECT_EQ(count, 3u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(vec4_f32, &count), f32);
     EXPECT_EQ(count, 4u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(vec3_u32, &count), u32);
     EXPECT_EQ(count, 3u);
-    count = 0;
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(vec3_i32, &count), i32);
     EXPECT_EQ(count, 3u);
-    count = 0;
-    EXPECT_TYPE(Type::ElementOf(mat2x4_f32, &count), f32);
-    EXPECT_EQ(count, 8u);
-    count = 0;
-    EXPECT_TYPE(Type::ElementOf(mat4x2_f32, &count), f32);
-    EXPECT_EQ(count, 8u);
-    count = 0;
-    EXPECT_TYPE(Type::ElementOf(mat4x3_f16, &count), f16);
-    EXPECT_EQ(count, 12u);
-    count = 0;
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(mat2x4_f32, &count), vec4_f32);
+    EXPECT_EQ(count, 2u);
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(mat4x2_f32, &count), vec2_f32);
+    EXPECT_EQ(count, 4u);
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(mat4x3_f16, &count), vec3_f16);
+    EXPECT_EQ(count, 4u);
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(str, &count), nullptr);
+    EXPECT_EQ(count, 0u);
+    count = 42;
     EXPECT_TYPE(Type::ElementOf(arr_i32, &count), i32);
     EXPECT_EQ(count, 5u);
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(arr_vec3_i32, &count), vec3_i32);
+    EXPECT_EQ(count, 5u);
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(arr_mat4x3_f16, &count), mat4x3_f16);
+    EXPECT_EQ(count, 5u);
+    count = 42;
+    EXPECT_TYPE(Type::ElementOf(arr_str, &count), str);
+    EXPECT_EQ(count, 5u);
+}
+
+TEST_F(TypeTest, DeepestElementOf) {
+    auto* f32 = create<F32>();
+    auto* f16 = create<F16>();
+    auto* i32 = create<I32>();
+    auto* u32 = create<U32>();
+    auto* vec2_f32 = create<Vector>(f32, 2u);
+    auto* vec3_f16 = create<Vector>(f16, 3u);
+    auto* vec4_f32 = create<Vector>(f32, 4u);
+    auto* vec3_u32 = create<Vector>(u32, 3u);
+    auto* vec3_i32 = create<Vector>(i32, 3u);
+    auto* mat2x4_f32 = create<Matrix>(vec4_f32, 2u);
+    auto* mat4x2_f32 = create<Matrix>(vec2_f32, 4u);
+    auto* mat4x3_f16 = create<Matrix>(vec3_f16, 4u);
+    auto* str = create<Struct>(nullptr, Sym("s"),
+                               StructMemberList{
+                                   create<StructMember>(
+                                       /* declaration */ nullptr,
+                                       /* name */ Sym("x"),
+                                       /* type */ f16,
+                                       /* index */ 0u,
+                                       /* offset */ 0u,
+                                       /* align */ 4u,
+                                       /* size */ 4u),
+                               },
+                               /* align*/ 4u,
+                               /* size*/ 4u,
+                               /* size_no_padding*/ 4u);
+    auto* arr_i32 = create<Array>(
+        /* element */ i32,
+        /* count */ 5u,
+        /* align */ 4u,
+        /* size */ 5u * 4u,
+        /* stride */ 5u * 4u,
+        /* implicit_stride */ 5u * 4u);
+    auto* arr_vec3_i32 = create<Array>(
+        /* element */ vec3_i32,
+        /* count */ 5u,
+        /* align */ 16u,
+        /* size */ 5u * 16u,
+        /* stride */ 5u * 16u,
+        /* implicit_stride */ 5u * 16u);
+    auto* arr_mat4x3_f16 = create<Array>(
+        /* element */ mat4x3_f16,
+        /* count */ 5u,
+        /* align */ 64u,
+        /* size */ 5u * 64u,
+        /* stride */ 5u * 64u,
+        /* implicit_stride */ 5u * 64u);
+    auto* arr_str = create<Array>(
+        /* element */ str,
+        /* count */ 5u,
+        /* align */ 4u,
+        /* size */ 5u * 4u,
+        /* stride */ 5u * 4u,
+        /* implicit_stride */ 5u * 4u);
+
+    // No count
+    EXPECT_TYPE(Type::DeepestElementOf(f32), f32);
+    EXPECT_TYPE(Type::DeepestElementOf(f16), f16);
+    EXPECT_TYPE(Type::DeepestElementOf(i32), i32);
+    EXPECT_TYPE(Type::DeepestElementOf(u32), u32);
+    EXPECT_TYPE(Type::DeepestElementOf(vec2_f32), f32);
+    EXPECT_TYPE(Type::DeepestElementOf(vec3_f16), f16);
+    EXPECT_TYPE(Type::DeepestElementOf(vec4_f32), f32);
+    EXPECT_TYPE(Type::DeepestElementOf(vec3_u32), u32);
+    EXPECT_TYPE(Type::DeepestElementOf(vec3_i32), i32);
+    EXPECT_TYPE(Type::DeepestElementOf(mat2x4_f32), f32);
+    EXPECT_TYPE(Type::DeepestElementOf(mat4x2_f32), f32);
+    EXPECT_TYPE(Type::DeepestElementOf(mat4x3_f16), f16);
+    EXPECT_TYPE(Type::DeepestElementOf(str), nullptr);
+    EXPECT_TYPE(Type::DeepestElementOf(arr_i32), i32);
+    EXPECT_TYPE(Type::DeepestElementOf(arr_vec3_i32), i32);
+    EXPECT_TYPE(Type::DeepestElementOf(arr_mat4x3_f16), f16);
+    EXPECT_TYPE(Type::DeepestElementOf(arr_str), nullptr);
+
+    // With count
+    uint32_t count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(f32, &count), f32);
+    EXPECT_EQ(count, 1u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(f16, &count), f16);
+    EXPECT_EQ(count, 1u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(i32, &count), i32);
+    EXPECT_EQ(count, 1u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(u32, &count), u32);
+    EXPECT_EQ(count, 1u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(vec2_f32, &count), f32);
+    EXPECT_EQ(count, 2u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(vec3_f16, &count), f16);
+    EXPECT_EQ(count, 3u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(vec4_f32, &count), f32);
+    EXPECT_EQ(count, 4u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(vec3_u32, &count), u32);
+    EXPECT_EQ(count, 3u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(vec3_i32, &count), i32);
+    EXPECT_EQ(count, 3u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(mat2x4_f32, &count), f32);
+    EXPECT_EQ(count, 8u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(mat4x2_f32, &count), f32);
+    EXPECT_EQ(count, 8u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(mat4x3_f16, &count), f16);
+    EXPECT_EQ(count, 12u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(str, &count), nullptr);
+    EXPECT_EQ(count, 0u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(arr_i32, &count), i32);
+    EXPECT_EQ(count, 5u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(arr_vec3_i32, &count), i32);
+    EXPECT_EQ(count, 15u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(arr_mat4x3_f16, &count), f16);
+    EXPECT_EQ(count, 60u);
+    count = 42;
+    EXPECT_TYPE(Type::DeepestElementOf(arr_str, &count), nullptr);
+    EXPECT_EQ(count, 0u);
 }
 
 TEST_F(TypeTest, Common2) {
diff --git a/src/tint/sem/variable.cc b/src/tint/sem/variable.cc
index de5cfa4..3849807 100644
--- a/src/tint/sem/variable.cc
+++ b/src/tint/sem/variable.cc
@@ -17,7 +17,9 @@
 #include <utility>
 
 #include "src/tint/ast/identifier_expression.h"
+#include "src/tint/ast/parameter.h"
 #include "src/tint/ast/variable.h"
+#include "src/tint/sem/pointer.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::sem::Variable);
 TINT_INSTANTIATE_TYPEINFO(tint::sem::GlobalVariable);
@@ -31,7 +33,7 @@
                    const sem::Type* type,
                    ast::StorageClass storage_class,
                    ast::Access access,
-                   Constant constant_value)
+                   const Constant* constant_value)
     : declaration_(declaration),
       type_(type),
       storage_class_(storage_class),
@@ -45,9 +47,8 @@
                              ast::StorageClass storage_class,
                              ast::Access access,
                              const sem::Statement* statement,
-                             Constant constant_value)
-    : Base(declaration, type, storage_class, access, std::move(constant_value)),
-      statement_(statement) {}
+                             const Constant* constant_value)
+    : Base(declaration, type, storage_class, access, constant_value), statement_(statement) {}
 
 LocalVariable::~LocalVariable() = default;
 
@@ -55,9 +56,9 @@
                                const sem::Type* type,
                                ast::StorageClass storage_class,
                                ast::Access access,
-                               Constant constant_value,
+                               const Constant* constant_value,
                                sem::BindingPoint binding_point)
-    : Base(declaration, type, storage_class, access, std::move(constant_value)),
+    : Base(declaration, type, storage_class, access, constant_value),
       binding_point_(binding_point) {}
 
 GlobalVariable::~GlobalVariable() = default;
@@ -68,7 +69,7 @@
                      ast::StorageClass storage_class,
                      ast::Access access,
                      const ParameterUsage usage /* = ParameterUsage::kNone */)
-    : Base(declaration, type, storage_class, access, Constant{}), index_(index), usage_(usage) {}
+    : Base(declaration, type, storage_class, access, nullptr), index_(index), usage_(usage) {}
 
 Parameter::~Parameter() = default;
 
@@ -89,4 +90,6 @@
     }
 }
 
+VariableUser::~VariableUser() = default;
+
 }  // namespace tint::sem
diff --git a/src/tint/sem/variable.h b/src/tint/sem/variable.h
index b995233..28e8f97 100644
--- a/src/tint/sem/variable.h
+++ b/src/tint/sem/variable.h
@@ -27,6 +27,7 @@
 // Forward declarations
 namespace tint::ast {
 class IdentifierExpression;
+class Parameter;
 class Variable;
 }  // namespace tint::ast
 namespace tint::sem {
@@ -51,7 +52,7 @@
              const sem::Type* type,
              ast::StorageClass storage_class,
              ast::Access access,
-             Constant constant_value);
+             const Constant* constant_value);
 
     /// Destructor
     ~Variable() override;
@@ -69,7 +70,7 @@
     ast::Access Access() const { return access_; }
 
     /// @return the constant value of this expression
-    const Constant& ConstantValue() const { return constant_value_; }
+    const Constant* ConstantValue() const { return constant_value_; }
 
     /// @returns the variable constructor expression, or nullptr if the variable
     /// does not have one.
@@ -90,7 +91,7 @@
     const sem::Type* const type_;
     const ast::StorageClass storage_class_;
     const ast::Access access_;
-    const Constant constant_value_;
+    const Constant* constant_value_;
     const Expression* constructor_ = nullptr;
     std::vector<const VariableUser*> users_;
 };
@@ -110,7 +111,7 @@
                   ast::StorageClass storage_class,
                   ast::Access access,
                   const sem::Statement* statement,
-                  Constant constant_value);
+                  const Constant* constant_value);
 
     /// Destructor
     ~LocalVariable() override;
@@ -144,7 +145,7 @@
                    const sem::Type* type,
                    ast::StorageClass storage_class,
                    ast::Access access,
-                   Constant constant_value,
+                   const Constant* constant_value,
                    sem::BindingPoint binding_point = {});
 
     /// Destructor
@@ -225,6 +226,7 @@
     VariableUser(const ast::IdentifierExpression* declaration,
                  Statement* statement,
                  sem::Variable* variable);
+    ~VariableUser() override;
 
     /// @returns the variable that this expression refers to
     const sem::Variable* Variable() const { return variable_; }
diff --git a/src/tint/tint.natvis b/src/tint/tint.natvis
index c1fd1f3..bce3ce5 100644
--- a/src/tint/tint.natvis
+++ b/src/tint/tint.natvis
@@ -101,11 +101,7 @@
 		<DisplayString>{*variable};</DisplayString>
 	</Type>
 
-	<Type Name="tint::ast::UintLiteralExpression">
-		<DisplayString>{value}</DisplayString>
-	</Type>
-
-	<Type Name="tint::ast::SintLiteralExpression">
+	<Type Name="tint::ast::IntLiteralExpression">
 		<DisplayString>{value}</DisplayString>
 	</Type>
 
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index 86c4534..81471ad 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -144,7 +144,7 @@
                 {ctx.dst->Member(kBufferSizeMemberName,
                                  ctx.dst->ty.array(ctx.dst->ty.vec4(ctx.dst->ty.u32()),
                                                    u32((max_buffer_size_index / 4) + 1)))});
-            buffer_size_ubo = ctx.dst->Global(
+            buffer_size_ubo = ctx.dst->GlobalVar(
                 ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct), ast::StorageClass::kUniform,
                 ast::AttributeList{
                     ctx.dst->GroupAndBinding(cfg->ubo_binding.group, cfg->ubo_binding.binding)});
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index 9bde1cc..b0f6297 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -44,6 +44,100 @@
     /// The source clone context
     const sem::Info& sem = ctx.src->Sem();
 
+    /// Builds the polyfill function for the `acosh` builtin
+    /// @param ty the parameter and return type for the function
+    /// @return the polyfill function name
+    Symbol acosh(const sem::Type* ty) {
+        auto name = b.Symbols().New("tint_acosh");
+        uint32_t width = WidthOf(ty);
+
+        auto V = [&](AFloat value) -> const ast::Expression* {
+            const ast::Expression* expr = b.Expr(value);
+            if (width == 1) {
+                return expr;
+            }
+            return b.Construct(T(ty), expr);
+        };
+
+        ast::StatementList body;
+        switch (polyfill.acosh) {
+            case Level::kFull:
+                // return log(x + sqrt(x*x - 1));
+                body.emplace_back(b.Return(
+                    b.Call("log", b.Add("x", b.Call("sqrt", b.Sub(b.Mul("x", "x"), 1_a))))));
+                break;
+            case Level::kRangeCheck: {
+                // return select(acosh(x), 0, x < 1);
+                body.emplace_back(b.Return(
+                    b.Call("select", b.Call("acosh", "x"), V(0.0_a), b.LessThan("x", V(1.0_a)))));
+                break;
+            }
+            default:
+                TINT_ICE(Transform, b.Diagnostics())
+                    << "unhandled polyfill level: " << static_cast<int>(polyfill.acosh);
+                return {};
+        }
+
+        b.Func(name, {b.Param("x", T(ty))}, T(ty), body);
+
+        return name;
+    }
+
+    /// Builds the polyfill function for the `asinh` builtin
+    /// @param ty the parameter and return type for the function
+    /// @return the polyfill function name
+    Symbol asinh(const sem::Type* ty) {
+        auto name = b.Symbols().New("tint_sinh");
+
+        ast::StatementList body;
+
+        // return log(x + sqrt(x*x + 1));
+        body.emplace_back(
+            b.Return(b.Call("log", b.Add("x", b.Call("sqrt", b.Add(b.Mul("x", "x"), 1_a))))));
+
+        b.Func(name, {b.Param("x", T(ty))}, T(ty), body);
+
+        return name;
+    }
+
+    /// Builds the polyfill function for the `atanh` builtin
+    /// @param ty the parameter and return type for the function
+    /// @return the polyfill function name
+    Symbol atanh(const sem::Type* ty) {
+        auto name = b.Symbols().New("tint_atanh");
+        uint32_t width = WidthOf(ty);
+
+        auto V = [&](AFloat value) -> const ast::Expression* {
+            const ast::Expression* expr = b.Expr(value);
+            if (width == 1) {
+                return expr;
+            }
+            return b.Construct(T(ty), expr);
+        };
+
+        ast::StatementList body;
+        switch (polyfill.atanh) {
+            case Level::kFull:
+                // return log((1+x) / (1-x)) * 0.5
+                body.emplace_back(
+                    b.Return(b.Mul(b.Call("log", b.Div(b.Add(1_a, "x"), b.Sub(1_a, "x"))), 0.5_a)));
+                break;
+            case Level::kRangeCheck:
+                // return select(atanh(x), 0, x >= 1);
+                body.emplace_back(b.Return(b.Call("select", b.Call("atanh", "x"), V(0.0_a),
+                                                  b.GreaterThanEqual("x", V(1.0_a)))));
+                break;
+            default:
+                TINT_ICE(Transform, b.Diagnostics())
+                    << "unhandled polyfill level: " << static_cast<int>(polyfill.acosh);
+                return {};
+        }
+
+        b.Func(name, {b.Param("x", T(ty))}, T(ty), body);
+
+        return name;
+    }
+
     /// Builds the polyfill function for the `countLeadingZeros` builtin
     /// @param ty the parameter and return type for the function
     /// @return the polyfill function name
@@ -440,6 +534,21 @@
             if (auto* call = sem.Get<sem::Call>(node)) {
                 if (auto* builtin = call->Target()->As<sem::Builtin>()) {
                     switch (builtin->Type()) {
+                        case sem::BuiltinType::kAcosh:
+                            if (builtins.acosh != Level::kNone) {
+                                return true;
+                            }
+                            break;
+                        case sem::BuiltinType::kAsinh:
+                            if (builtins.asinh) {
+                                return true;
+                            }
+                            break;
+                        case sem::BuiltinType::kAtanh:
+                            if (builtins.atanh != Level::kNone) {
+                                return true;
+                            }
+                            break;
                         case sem::BuiltinType::kCountLeadingZeros:
                             if (builtins.count_leading_zeros) {
                                 return true;
@@ -496,6 +605,24 @@
             if (auto* builtin = call->Target()->As<sem::Builtin>()) {
                 Symbol polyfill;
                 switch (builtin->Type()) {
+                    case sem::BuiltinType::kAcosh:
+                        if (builtins.acosh != Level::kNone) {
+                            polyfill = utils::GetOrCreate(
+                                polyfills, builtin, [&] { return s.acosh(builtin->ReturnType()); });
+                        }
+                        break;
+                    case sem::BuiltinType::kAsinh:
+                        if (builtins.asinh) {
+                            polyfill = utils::GetOrCreate(
+                                polyfills, builtin, [&] { return s.asinh(builtin->ReturnType()); });
+                        }
+                        break;
+                    case sem::BuiltinType::kAtanh:
+                        if (builtins.atanh != Level::kNone) {
+                            polyfill = utils::GetOrCreate(
+                                polyfills, builtin, [&] { return s.atanh(builtin->ReturnType()); });
+                        }
+                        break;
                     case sem::BuiltinType::kCountLeadingZeros:
                         if (builtins.count_leading_zeros) {
                             polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
diff --git a/src/tint/transform/builtin_polyfill.h b/src/tint/transform/builtin_polyfill.h
index 8453189..8df4197 100644
--- a/src/tint/transform/builtin_polyfill.h
+++ b/src/tint/transform/builtin_polyfill.h
@@ -33,12 +33,20 @@
         kNone,
         /// Clamp the parameters to the inner implementation.
         kClampParameters,
+        /// Range check the input.
+        kRangeCheck,
         /// Polyfill the entire function
         kFull,
     };
 
     /// Specifies the builtins that should be polyfilled by the transform.
     struct Builtins {
+        /// What level should `acosh` be polyfilled?
+        Level acosh = Level::kNone;
+        /// Should `asinh` be polyfilled?
+        bool asinh = false;
+        /// What level should `atanh` be polyfilled?
+        Level atanh = Level::kNone;
         /// Should `countLeadingZeros()` be polyfilled?
         bool count_leading_zeros = false;
         /// Should `countTrailingZeros()` be polyfilled?
diff --git a/src/tint/transform/builtin_polyfill_test.cc b/src/tint/transform/builtin_polyfill_test.cc
index bc3dda8..1292b65 100644
--- a/src/tint/transform/builtin_polyfill_test.cc
+++ b/src/tint/transform/builtin_polyfill_test.cc
@@ -42,6 +42,292 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// acosh
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillAcosh(Level level) {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.acosh = level;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunAcosh) {
+    auto* src = R"(
+fn f() {
+  acosh(1.0);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kNone)));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kClampParameters)));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
+}
+
+TEST_F(BuiltinPolyfillTest, Acosh_Full_f32) {
+    auto* src = R"(
+fn f() {
+  let r : f32 = acosh(1234);
+}
+)";
+
+    auto* expect = R"(
+fn tint_acosh(x : f32) -> f32 {
+  return log((x + sqrt(((x * x) - 1))));
+}
+
+fn f() {
+  let r : f32 = tint_acosh(1234);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Acosh_Full_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let r : vec3<f32> = acosh(vec3<f32>(1234));
+}
+)";
+
+    auto* expect = R"(
+fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
+  return log((x + sqrt(((x * x) - 1))));
+}
+
+fn f() {
+  let r : vec3<f32> = tint_acosh(vec3<f32>(1234));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Acosh_Range_f32) {
+    auto* src = R"(
+fn f() {
+  let r : f32 = acosh(1234);
+}
+)";
+
+    auto* expect = R"(
+fn tint_acosh(x : f32) -> f32 {
+  return select(acosh(x), 0.0, (x < 1.0));
+}
+
+fn f() {
+  let r : f32 = tint_acosh(1234);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Acosh_Range_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let r : vec3<f32> = acosh(vec3<f32>(1234));
+}
+)";
+
+    auto* expect = R"(
+fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
+  return select(acosh(x), vec3<f32>(0.0), (x < vec3<f32>(1.0)));
+}
+
+fn f() {
+  let r : vec3<f32> = tint_acosh(vec3<f32>(1234));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// asinh
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillSinh() {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.asinh = true;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunAsinh) {
+    auto* src = R"(
+fn f() {
+  asinh(1.0);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSinh()));
+}
+
+TEST_F(BuiltinPolyfillTest, Asinh_f32) {
+    auto* src = R"(
+fn f() {
+  let r : f32 = asinh(1234);
+}
+)";
+
+    auto* expect = R"(
+fn tint_sinh(x : f32) -> f32 {
+  return log((x + sqrt(((x * x) + 1))));
+}
+
+fn f() {
+  let r : f32 = tint_sinh(1234);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Asinh_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let r : vec3<f32> = asinh(vec3<f32>(1234));
+}
+)";
+
+    auto* expect = R"(
+fn tint_sinh(x : vec3<f32>) -> vec3<f32> {
+  return log((x + sqrt(((x * x) + 1))));
+}
+
+fn f() {
+  let r : vec3<f32> = tint_sinh(vec3<f32>(1234));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// atanh
+////////////////////////////////////////////////////////////////////////////////
+DataMap polyfillAtanh(Level level) {
+    BuiltinPolyfill::Builtins builtins;
+    builtins.atanh = level;
+    DataMap data;
+    data.Add<BuiltinPolyfill::Config>(builtins);
+    return data;
+}
+
+TEST_F(BuiltinPolyfillTest, ShouldRunAtanh) {
+    auto* src = R"(
+fn f() {
+  atanh(1.0);
+}
+)";
+
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
+    EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kNone)));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kClampParameters)));
+    EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
+}
+
+TEST_F(BuiltinPolyfillTest, Atanh_Full_f32) {
+    auto* src = R"(
+fn f() {
+  let r : f32 = atanh(1234);
+}
+)";
+
+    auto* expect = R"(
+fn tint_atanh(x : f32) -> f32 {
+  return (log(((1 + x) / (1 - x))) * 0.5);
+}
+
+fn f() {
+  let r : f32 = tint_atanh(1234);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Atanh_Full_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let r : vec3<f32> = atanh(vec3<f32>(1234));
+}
+)";
+
+    auto* expect = R"(
+fn tint_atanh(x : vec3<f32>) -> vec3<f32> {
+  return (log(((1 + x) / (1 - x))) * 0.5);
+}
+
+fn f() {
+  let r : vec3<f32> = tint_atanh(vec3<f32>(1234));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Atanh_Range_f32) {
+    auto* src = R"(
+fn f() {
+  let r : f32 = atanh(1234);
+}
+)";
+
+    auto* expect = R"(
+fn tint_atanh(x : f32) -> f32 {
+  return select(atanh(x), 0.0, (x >= 1.0));
+}
+
+fn f() {
+  let r : f32 = tint_atanh(1234);
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(BuiltinPolyfillTest, Atanh_Range_vec3_f32) {
+    auto* src = R"(
+fn f() {
+  let r : vec3<f32> = atanh(vec3<f32>(1234));
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let r : vec3<f32> = atanh(vec3<f32>(1234));
+}
+)";
+
+    auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
+
+    EXPECT_EQ(expect, str(got));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // countLeadingZeros
 ////////////////////////////////////////////////////////////////////////////////
 DataMap polyfillCountLeadingZeros() {
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index 104429b..a324bcc 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -196,7 +196,7 @@
                     value = ctx.dst->IndexAccessor(value, 0_i);
                 }
             }
-            ctx.dst->Global(symbol, ast_type, ast::StorageClass::kInput, std::move(attributes));
+            ctx.dst->GlobalVar(symbol, ast_type, ast::StorageClass::kInput, std::move(attributes));
             return value;
         } else if (cfg.shader_style == ShaderStyle::kMsl &&
                    ast::HasAttribute<ast::BuiltinAttribute>(attributes)) {
@@ -463,7 +463,7 @@
                 type = ctx.dst->ty.array(type, 1_u);
                 lhs = ctx.dst->IndexAccessor(lhs, 0_i);
             }
-            ctx.dst->Global(name, type, ast::StorageClass::kOutput, std::move(attributes));
+            ctx.dst->GlobalVar(name, type, ast::StorageClass::kOutput, std::move(attributes));
             wrapper_body.push_back(ctx.dst->Assign(lhs, outval.value));
         }
     }
diff --git a/src/tint/transform/combine_samplers.cc b/src/tint/transform/combine_samplers.cc
index cd8395c..4e31789 100644
--- a/src/tint/transform/combine_samplers.cc
+++ b/src/tint/transform/combine_samplers.cc
@@ -19,6 +19,7 @@
 #include <utility>
 #include <vector>
 
+#include "src/tint/program_builder.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/statement.h"
 
@@ -109,7 +110,7 @@
         }
         const ast::Type* type = CreateCombinedASTTypeFor(texture_var, sampler_var);
         Symbol symbol = ctx.dst->Symbols().New(name);
-        return ctx.dst->Global(symbol, type, Attributes());
+        return ctx.dst->GlobalVar(symbol, type, Attributes());
     }
 
     /// Creates placeholder global sampler variables.
@@ -121,7 +122,7 @@
                                ? "placeholder_comparison_sampler"
                                : "placeholder_sampler";
         Symbol symbol = ctx.dst->Symbols().New(name);
-        return ctx.dst->Global(symbol, type, Attributes());
+        return ctx.dst->GlobalVar(symbol, type, Attributes());
     }
 
     /// Creates ast::Type for a given texture and sampler variable pair.
@@ -224,19 +225,21 @@
                 // Replace all texture builtin calls.
                 if (auto* builtin = call->Target()->As<sem::Builtin>()) {
                     const auto& signature = builtin->Signature();
-                    int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
-                    int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
+                    auto sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
+                    auto texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
                     if (texture_index == -1) {
                         return nullptr;
                     }
-                    const sem::Expression* texture = call->Arguments()[texture_index];
+                    const sem::Expression* texture =
+                        call->Arguments()[static_cast<size_t>(texture_index)];
                     // We don't want to combine storage textures with anything, since
                     // they never have associated samplers in GLSL.
                     if (texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
                         return nullptr;
                     }
                     const sem::Expression* sampler =
-                        sampler_index != -1 ? call->Arguments()[sampler_index] : nullptr;
+                        sampler_index != -1 ? call->Arguments()[static_cast<size_t>(sampler_index)]
+                                            : nullptr;
                     auto* texture_var = texture->As<sem::VariableUser>()->Variable();
                     auto* sampler_var =
                         sampler ? sampler->As<sem::VariableUser>()->Variable() : nullptr;
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 32ad153..48dae27 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -377,10 +377,10 @@
         auto* lhs_lit = tint::As<OffsetLiteral>(lhs);
         auto* rhs_lit = tint::As<OffsetLiteral>(rhs);
         if (lhs_lit && lhs_lit->literal == 0) {
-            return offsets_.Create<OffsetLiteral>(0);
+            return offsets_.Create<OffsetLiteral>(0u);
         }
         if (rhs_lit && rhs_lit->literal == 0) {
-            return offsets_.Create<OffsetLiteral>(0);
+            return offsets_.Create<OffsetLiteral>(0u);
         }
         if (lhs_lit && lhs_lit->literal == 1) {
             return rhs;
@@ -831,7 +831,7 @@
                 if (swizzle->Indices().size() == 1) {
                     if (auto access = state.TakeAccess(accessor->structure)) {
                         auto* vec_ty = access.type->As<sem::Vector>();
-                        auto* offset = state.Mul(vec_ty->type()->Size(), swizzle->Indices()[0]);
+                        auto* offset = state.Mul(vec_ty->type()->Size(), swizzle->Indices()[0u]);
                         state.AddAccess(accessor, {
                                                       access.var,
                                                       state.Add(access.offset, offset),
diff --git a/src/tint/transform/decompose_strided_array_test.cc b/src/tint/transform/decompose_strided_array_test.cc
index ffc6695..65b394a 100644
--- a/src/tint/transform/decompose_strided_array_test.cc
+++ b/src/tint/transform/decompose_strided_array_test.cc
@@ -39,7 +39,7 @@
     // var<private> arr : array<f32, 4u>
 
     ProgramBuilder b;
-    b.Global("arr", b.ty.array<f32, 4u>(), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(), ast::StorageClass::kPrivate);
     EXPECT_FALSE(ShouldRun<DecomposeStridedArray>(Program(std::move(b))));
 }
 
@@ -47,7 +47,7 @@
     // var<private> arr : @stride(4) array<f32, 4u>
 
     ProgramBuilder b;
-    b.Global("arr", b.ty.array<f32, 4u>(4), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(4), ast::StorageClass::kPrivate);
     EXPECT_TRUE(ShouldRun<DecomposeStridedArray>(Program(std::move(b))));
 }
 
@@ -55,7 +55,7 @@
     // var<private> arr : @stride(16) array<f32, 4u>
 
     ProgramBuilder b;
-    b.Global("arr", b.ty.array<f32, 4u>(16), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(16), ast::StorageClass::kPrivate);
     EXPECT_TRUE(ShouldRun<DecomposeStridedArray>(Program(std::move(b))));
 }
 
@@ -78,7 +78,7 @@
     // }
 
     ProgramBuilder b;
-    b.Global("arr", b.ty.array<f32, 4u>(4), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(4), ast::StorageClass::kPrivate);
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(4), b.Expr("arr"))),
@@ -114,7 +114,7 @@
     // }
 
     ProgramBuilder b;
-    b.Global("arr", b.ty.array<f32, 4u>(32), ast::StorageClass::kPrivate);
+    b.GlobalVar("arr", b.ty.array<f32, 4u>(32), ast::StorageClass::kPrivate);
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(32), b.Expr("arr"))),
@@ -158,7 +158,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(32), b.MemberAccessor("s", "a"))),
@@ -206,7 +206,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array(b.ty.vec4<f32>(), 4_u, 16))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
     b.Func(
         "f", {}, b.ty.void_(),
         {
@@ -252,7 +252,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(32), b.MemberAccessor("s", "a"))),
@@ -300,7 +300,7 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array<f32, 4u>(4))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.array<f32, 4u>(4), b.MemberAccessor("s", "a"))),
@@ -344,8 +344,8 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Assign(b.MemberAccessor("s", "a"), b.Construct(b.ty.array<f32, 4u>(32))),
@@ -398,8 +398,8 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array<f32, 4u>(4))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Assign(b.MemberAccessor("s", "a"), b.Construct(b.ty.array<f32, 4u>(4))),
@@ -450,8 +450,8 @@
     // }
     ProgramBuilder b;
     auto* S = b.Structure("S", {b.Member("a", b.ty.array<f32, 4u>(32))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", nullptr, b.AddressOf(b.MemberAccessor("s", "a")))),
@@ -511,8 +511,8 @@
     ProgramBuilder b;
     b.Alias("ARR", b.ty.array<f32, 4u>(32));
     auto* S = b.Structure("S", {b.Member("a", b.ty.type_name("ARR"))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.type_name("ARR"), b.MemberAccessor("s", "a"))),
@@ -581,8 +581,8 @@
                 b.ty.array(b.ty.type_name("ARR_A"), 3_u, 16),  //
                 4_u, 128));
     auto* S = b.Structure("S", {b.Member("a", b.ty.type_name("ARR_B"))});
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", b.ty.type_name("ARR_B"), b.MemberAccessor("s", "a"))),
diff --git a/src/tint/transform/decompose_strided_matrix_test.cc b/src/tint/transform/decompose_strided_matrix_test.cc
index 06169d7..daff16f 100644
--- a/src/tint/transform/decompose_strided_matrix_test.cc
+++ b/src/tint/transform/decompose_strided_matrix_test.cc
@@ -71,12 +71,12 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(16),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(16u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -127,12 +127,12 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(16),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(16u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
     b.Func(
         "f", {}, b.ty.void_(),
         {
@@ -180,12 +180,12 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(16),
-                              b.create<ast::StrideAttribute>(8),
+                              b.create<ast::StructMemberOffsetAttribute>(16u),
+                              b.create<ast::StrideAttribute>(8u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -233,13 +233,13 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(8),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(8u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -290,13 +290,13 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(16),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(16u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func(
         "f", {}, b.ty.void_(),
         {
@@ -344,13 +344,13 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(8),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(8u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Assign(b.MemberAccessor("s", "m"),
@@ -402,13 +402,13 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(8),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(8u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Assign(b.IndexAccessor(b.MemberAccessor("s", "m"), 1_i), b.vec2<f32>(1_f, 2_f)),
@@ -461,13 +461,13 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(8),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(8u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-             b.GroupAndBinding(0, 0));
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                b.GroupAndBinding(0, 0));
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("a", nullptr, b.AddressOf(b.MemberAccessor("s", "m")))),
@@ -532,12 +532,12 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(8),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(8u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kPrivate);
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kPrivate);
     b.Func("f", {}, b.ty.void_(),
            {
                b.Decl(b.Let("x", b.ty.mat2x2<f32>(), b.MemberAccessor("s", "m"))),
@@ -585,12 +585,12 @@
         "S", {
                  b.Member("m", b.ty.mat2x2<f32>(),
                           {
-                              b.create<ast::StructMemberOffsetAttribute>(8),
-                              b.create<ast::StrideAttribute>(32),
+                              b.create<ast::StructMemberOffsetAttribute>(8u),
+                              b.create<ast::StrideAttribute>(32u),
                               b.Disable(ast::DisabledValidation::kIgnoreStrideAttribute),
                           }),
              });
-    b.Global("s", b.ty.Of(S), ast::StorageClass::kPrivate);
+    b.GlobalVar("s", b.ty.Of(S), ast::StorageClass::kPrivate);
     b.Func("f", {}, b.ty.void_(),
            {
                b.Assign(b.MemberAccessor("s", "m"),
diff --git a/src/tint/transform/first_index_offset.cc b/src/tint/transform/first_index_offset.cc
index 9d89b83..01f9771 100644
--- a/src/tint/transform/first_index_offset.cc
+++ b/src/tint/transform/first_index_offset.cc
@@ -121,11 +121,12 @@
 
         // Create a global to hold the uniform buffer
         Symbol buffer_name = ctx.dst->Sym();
-        ctx.dst->Global(buffer_name, ctx.dst->ty.Of(struct_), ast::StorageClass::kUniform, nullptr,
-                        ast::AttributeList{
-                            ctx.dst->create<ast::BindingAttribute>(ub_binding),
-                            ctx.dst->create<ast::GroupAttribute>(ub_group),
-                        });
+        ctx.dst->GlobalVar(buffer_name, ctx.dst->ty.Of(struct_), ast::StorageClass::kUniform,
+                           nullptr,
+                           ast::AttributeList{
+                               ctx.dst->create<ast::BindingAttribute>(ub_binding),
+                               ctx.dst->create<ast::GroupAttribute>(ub_group),
+                           });
 
         // Fix up all references to the builtins with the offsets
         ctx.ReplaceAll([=, &ctx](const ast::Expression* expr) -> const ast::Expression* {
diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc
index d6cdded..7c3d695 100644
--- a/src/tint/transform/localize_struct_array_assignment.cc
+++ b/src/tint/transform/localize_struct_array_assignment.cc
@@ -46,7 +46,7 @@
             expr, b.Diagnostics(), [&](const ast::IndexAccessorExpression* ia) {
                 // Indexing using a runtime value?
                 auto* idx_sem = ctx.src->Sem().Get(ia->index);
-                if (!idx_sem->ConstantValue().IsValid()) {
+                if (!idx_sem->ConstantValue()) {
                     // Indexing a member access expr?
                     if (auto* ma = ia->object->As<ast::MemberAccessorExpression>()) {
                         // That accesses an array?
diff --git a/src/tint/transform/loop_to_for_loop_test.cc b/src/tint/transform/loop_to_for_loop_test.cc
index e3d7ecc..24e0e15 100644
--- a/src/tint/transform/loop_to_for_loop_test.cc
+++ b/src/tint/transform/loop_to_for_loop_test.cc
@@ -151,7 +151,7 @@
 )";
 
     auto* expect = R"(
-let N = 16u;
+const N = 16u;
 
 fn f() {
   var i : u32 = 0u;
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.cc b/src/tint/transform/module_scope_var_to_entry_point_param.cc
index cc89fde..5a7eecc 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.cc
@@ -23,6 +23,7 @@
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/function.h"
+#include "src/tint/sem/module.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/variable.h"
 
@@ -30,6 +31,10 @@
 
 namespace tint::transform {
 namespace {
+
+// The name of the struct member for arrays that are wrapped in structures.
+const char* kWrappedArrayMemberName = "arr";
+
 // Returns `true` if `type` is or contains a matrix type.
 bool ContainsMatrix(const sem::Type* type) {
     type = type->UnwrapRef();
@@ -83,6 +88,192 @@
         }
     }
 
+    /// Process a variable `var` that is referenced in the entry point function `func`.
+    /// This will redeclare the variable as a function parameter, possibly as a pointer.
+    /// Some workgroup variables will be redeclared as a member inside a workgroup structure.
+    /// @param func the entry point function
+    /// @param var the variable
+    /// @param new_var_symbol the symbol to use for the replacement
+    /// @param workgroup_param helper function to get a symbol to a workgroup struct parameter
+    /// @param workgroup_parameter_members reference to a list of a workgroup struct members
+    /// @param is_pointer output signalling whether the replacement is a pointer
+    /// @param is_wrapped output signalling whether the replacement is wrapped in a struct
+    void ProcessVariableInEntryPoint(const ast::Function* func,
+                                     const sem::Variable* var,
+                                     Symbol new_var_symbol,
+                                     std::function<Symbol()> workgroup_param,
+                                     ast::StructMemberList& workgroup_parameter_members,
+                                     bool& is_pointer,
+                                     bool& is_wrapped) {
+        auto* var_ast = var->Declaration()->As<ast::Var>();
+        auto* ty = var->Type()->UnwrapRef();
+
+        // Helper to create an AST node for the store type of the variable.
+        auto store_type = [&]() { return CreateASTTypeFor(ctx, ty); };
+
+        ast::StorageClass sc = var->StorageClass();
+        switch (sc) {
+            case ast::StorageClass::kHandle: {
+                // For a texture or sampler variable, redeclare it as an entry point parameter.
+                // Disable entry point parameter validation.
+                auto* disable_validation =
+                    ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter);
+                auto attrs = ctx.Clone(var->Declaration()->attributes);
+                attrs.push_back(disable_validation);
+                auto* param = ctx.dst->Param(new_var_symbol, store_type(), attrs);
+                ctx.InsertFront(func->params, param);
+
+                break;
+            }
+            case ast::StorageClass::kStorage:
+            case ast::StorageClass::kUniform: {
+                // Variables into the Storage and Uniform storage classes are redeclared as entry
+                // point parameters with a pointer type.
+                auto attributes = ctx.Clone(var->Declaration()->attributes);
+                attributes.push_back(
+                    ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter));
+                attributes.push_back(
+                    ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
+
+                auto* param_type = store_type();
+                if (auto* arr = ty->As<sem::Array>(); arr && arr->IsRuntimeSized()) {
+                    // Wrap runtime-sized arrays in structures, so that we can declare pointers to
+                    // them. Ideally we'd just emit the array itself as a pointer, but this is not
+                    // representable in Tint's AST.
+                    CloneStructTypes(ty);
+                    auto* wrapper = ctx.dst->Structure(
+                        ctx.dst->Sym(), {ctx.dst->Member(kWrappedArrayMemberName, param_type)});
+                    param_type = ctx.dst->ty.Of(wrapper);
+                    is_wrapped = true;
+                }
+
+                param_type = ctx.dst->ty.pointer(param_type, sc, var_ast->declared_access);
+                auto* param = ctx.dst->Param(new_var_symbol, param_type, attributes);
+                ctx.InsertFront(func->params, param);
+                is_pointer = true;
+
+                break;
+            }
+            case ast::StorageClass::kWorkgroup: {
+                if (ContainsMatrix(var->Type())) {
+                    // Due to a bug in the MSL compiler, we use a threadgroup memory argument for
+                    // any workgroup allocation that contains a matrix. See crbug.com/tint/938.
+                    // TODO(jrprice): Do this for all other workgroup variables too.
+
+                    // Create a member in the workgroup parameter struct.
+                    auto member = ctx.Clone(var->Declaration()->symbol);
+                    workgroup_parameter_members.push_back(ctx.dst->Member(member, store_type()));
+                    CloneStructTypes(var->Type()->UnwrapRef());
+
+                    // Create a function-scope variable that is a pointer to the member.
+                    auto* member_ptr = ctx.dst->AddressOf(
+                        ctx.dst->MemberAccessor(ctx.dst->Deref(workgroup_param()), member));
+                    auto* local_var = ctx.dst->Let(
+                        new_var_symbol,
+                        ctx.dst->ty.pointer(store_type(), ast::StorageClass::kWorkgroup),
+                        member_ptr);
+                    ctx.InsertFront(func->body->statements, ctx.dst->Decl(local_var));
+                    is_pointer = true;
+
+                    break;
+                }
+                [[fallthrough]];
+            }
+            case ast::StorageClass::kPrivate: {
+                // Variables in the Private and Workgroup storage classes are redeclared at function
+                // scope. Disable storage class validation on this variable.
+                auto* disable_validation =
+                    ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass);
+                auto* constructor = ctx.Clone(var->Declaration()->constructor);
+                auto* local_var = ctx.dst->Var(new_var_symbol, store_type(), sc, constructor,
+                                               ast::AttributeList{disable_validation});
+                ctx.InsertFront(func->body->statements, ctx.dst->Decl(local_var));
+
+                break;
+            }
+            default: {
+                TINT_ICE(Transform, ctx.dst->Diagnostics())
+                    << "unhandled module-scope storage class (" << sc << ")";
+            }
+        }
+    }
+
+    /// Process a variable `var` that is referenced in the user-defined function `func`.
+    /// This will redeclare the variable as a function parameter, possibly as a pointer.
+    /// @param func the user-defined function
+    /// @param var the variable
+    /// @param new_var_symbol the symbol to use for the replacement
+    /// @param is_pointer output signalling whether the replacement is a pointer or not
+    void ProcessVariableInUserFunction(const ast::Function* func,
+                                       const sem::Variable* var,
+                                       Symbol new_var_symbol,
+                                       bool& is_pointer) {
+        auto* var_ast = var->Declaration()->As<ast::Var>();
+        auto* ty = var->Type()->UnwrapRef();
+        auto* param_type = CreateASTTypeFor(ctx, ty);
+        auto sc = var->StorageClass();
+        switch (sc) {
+            case ast::StorageClass::kPrivate:
+            case ast::StorageClass::kStorage:
+            case ast::StorageClass::kUniform:
+            case ast::StorageClass::kHandle:
+            case ast::StorageClass::kWorkgroup:
+                break;
+            default:
+                TINT_ICE(Transform, ctx.dst->Diagnostics())
+                    << "unhandled module-scope storage class (" << sc << ")";
+        }
+
+        // Use a pointer for non-handle types.
+        ast::AttributeList attributes;
+        if (!ty->is_handle()) {
+            param_type = ctx.dst->ty.pointer(param_type, sc, var_ast->declared_access);
+            is_pointer = true;
+
+            // Disable validation of the parameter's storage class and of arguments passed to it.
+            attributes.push_back(ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
+            attributes.push_back(
+                ctx.dst->Disable(ast::DisabledValidation::kIgnoreInvalidPointerArgument));
+        }
+
+        // Redeclare the variable as a parameter.
+        ctx.InsertBack(func->params, ctx.dst->Param(new_var_symbol, param_type, attributes));
+    }
+
+    /// Replace all uses of `var` in `func` with references to `new_var`.
+    /// @param func the function
+    /// @param var the variable to replace
+    /// @param new_var the symbol to use for replacement
+    /// @param is_pointer true if `new_var` is a pointer to the new variable
+    /// @param is_wrapped true if `new_var` is an array wrapped in a structure
+    void ReplaceUsesInFunction(const ast::Function* func,
+                               const sem::Variable* var,
+                               Symbol new_var,
+                               bool is_pointer,
+                               bool is_wrapped) {
+        for (auto* user : var->Users()) {
+            if (user->Stmt()->Function()->Declaration() == func) {
+                const ast::Expression* expr = ctx.dst->Expr(new_var);
+                if (is_pointer) {
+                    // If this identifier is used by an address-of operator, just remove the
+                    // address-of instead of adding a deref, since we already have a pointer.
+                    auto* ident = user->Declaration()->As<ast::IdentifierExpression>();
+                    if (ident_to_address_of_.count(ident)) {
+                        ctx.Replace(ident_to_address_of_[ident], expr);
+                        continue;
+                    }
+
+                    expr = ctx.dst->Deref(expr);
+                }
+                if (is_wrapped) {
+                    // Get the member from the wrapper structure.
+                    expr = ctx.dst->MemberAccessor(expr, kWrappedArrayMemberName);
+                }
+                ctx.Replace(user->Declaration(), expr);
+            }
+        }
+    }
+
     /// Process the module.
     void Process() {
         // Predetermine the list of function calls that need to be replaced.
@@ -91,9 +282,13 @@
 
         std::vector<const ast::Function*> functions_to_process;
 
-        // Build a list of functions that transitively reference any module-scope
-        // variables.
-        for (auto* func_ast : ctx.src->AST().Functions()) {
+        // Build a list of functions that transitively reference any module-scope variables.
+        for (auto* decl : ctx.src->Sem().Module()->DependencyOrderedDeclarations()) {
+            auto* func_ast = decl->As<ast::Function>();
+            if (!func_ast) {
+                continue;
+            }
+
             auto* func_sem = ctx.src->Sem().Get(func_ast);
 
             bool needs_processing = false;
@@ -114,20 +309,18 @@
             }
         }
 
-        // Build a list of `&ident` expressions. We'll use this later to avoid
-        // generating expressions of the form `&*ident`, which break WGSL validation
-        // rules when this expression is passed to a function.
-        // TODO(jrprice): We should add support for bidirectional SEM tree traversal
-        // so that we can do this on the fly instead.
-        std::unordered_map<const ast::IdentifierExpression*, const ast::UnaryOpExpression*>
-            ident_to_address_of;
+        // Build a list of `&ident` expressions. We'll use this later to avoid generating
+        // expressions of the form `&*ident`, which break WGSL validation rules when this expression
+        // is passed to a function.
+        // TODO(jrprice): We should add support for bidirectional SEM tree traversal so that we can
+        // do this on the fly instead.
         for (auto* node : ctx.src->ASTNodes().Objects()) {
             auto* address_of = node->As<ast::UnaryOpExpression>();
             if (!address_of || address_of->op != ast::UnaryOp::kAddressOf) {
                 continue;
             }
             if (auto* ident = address_of->expr->As<ast::IdentifierExpression>()) {
-                ident_to_address_of[ident] = address_of;
+                ident_to_address_of_[ident] = address_of;
             }
         }
 
@@ -141,11 +334,10 @@
                 bool is_pointer;
                 bool is_wrapped;
             };
-            const char* kWrappedArrayMemberName = "arr";
             std::unordered_map<const sem::Variable*, NewVar> var_to_newvar;
 
-            // We aggregate all workgroup variables into a struct to avoid hitting
-            // MSL's limit for threadgroup memory arguments.
+            // We aggregate all workgroup variables into a struct to avoid hitting MSL's limit for
+            // threadgroup memory arguments.
             Symbol workgroup_parameter_symbol;
             ast::StructMemberList workgroup_parameter_members;
             auto workgroup_param = [&]() {
@@ -155,159 +347,69 @@
                 return workgroup_parameter_symbol;
             };
 
-            for (auto* global : func_sem->TransitivelyReferencedGlobals()) {
-                auto* var = global->Declaration()->As<ast::Var>();
-                if (!var) {
+            // Process and redeclare all variables referenced by the function.
+            for (auto* var : func_sem->TransitivelyReferencedGlobals()) {
+                if (var->StorageClass() == ast::StorageClass::kNone) {
                     continue;
                 }
-                auto sc = global->StorageClass();
-                auto* ty = global->Type()->UnwrapRef();
-                if (sc == ast::StorageClass::kNone) {
+                if (local_private_vars_.count(var)) {
                     continue;
                 }
-                if (sc != ast::StorageClass::kPrivate && sc != ast::StorageClass::kStorage &&
-                    sc != ast::StorageClass::kUniform && sc != ast::StorageClass::kHandle &&
-                    sc != ast::StorageClass::kWorkgroup) {
-                    TINT_ICE(Transform, ctx.dst->Diagnostics())
-                        << "unhandled module-scope storage class (" << sc << ")";
-                }
 
-                // This is the symbol for the variable that replaces the module-scope
-                // var.
+                // This is the symbol for the variable that replaces the module-scope var.
                 auto new_var_symbol = ctx.dst->Sym();
 
-                // Helper to create an AST node for the store type of the variable.
-                auto store_type = [&]() { return CreateASTTypeFor(ctx, ty); };
-
                 // Track whether the new variable is a pointer or not.
                 bool is_pointer = false;
 
                 // Track whether the new variable was wrapped in a struct or not.
                 bool is_wrapped = false;
 
-                if (is_entry_point) {
-                    if (global->Type()->UnwrapRef()->is_handle()) {
-                        // For a texture or sampler variable, redeclare it as an entry point
-                        // parameter. Disable entry point parameter validation.
-                        auto* disable_validation =
-                            ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter);
-                        auto attrs = ctx.Clone(var->attributes);
-                        attrs.push_back(disable_validation);
-                        auto* param = ctx.dst->Param(new_var_symbol, store_type(), attrs);
-                        ctx.InsertFront(func_ast->params, param);
-                    } else if (sc == ast::StorageClass::kStorage ||
-                               sc == ast::StorageClass::kUniform) {
-                        // Variables into the Storage and Uniform storage classes are
-                        // redeclared as entry point parameters with a pointer type.
-                        auto attributes = ctx.Clone(var->attributes);
-                        attributes.push_back(
-                            ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter));
-                        attributes.push_back(
-                            ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
-
-                        auto* param_type = store_type();
-                        if (auto* arr = ty->As<sem::Array>(); arr && arr->IsRuntimeSized()) {
-                            // Wrap runtime-sized arrays in structures, so that we can declare
-                            // pointers to them. Ideally we'd just emit the array itself as a
-                            // pointer, but this is not representable in Tint's AST.
-                            CloneStructTypes(ty);
-                            auto* wrapper = ctx.dst->Structure(
-                                ctx.dst->Sym(),
-                                {ctx.dst->Member(kWrappedArrayMemberName, param_type)});
-                            param_type = ctx.dst->ty.Of(wrapper);
-                            is_wrapped = true;
+                // Check if this is a private variable that is only referenced by this function.
+                bool local_private = false;
+                if (var->StorageClass() == ast::StorageClass::kPrivate) {
+                    local_private = true;
+                    for (auto* user : var->Users()) {
+                        auto* stmt = user->Stmt();
+                        if (!stmt || stmt->Function() != func_sem) {
+                            local_private = false;
+                            break;
                         }
-
-                        param_type = ctx.dst->ty.pointer(param_type, sc, var->declared_access);
-                        auto* param = ctx.dst->Param(new_var_symbol, param_type, attributes);
-                        ctx.InsertFront(func_ast->params, param);
-                        is_pointer = true;
-                    } else if (sc == ast::StorageClass::kWorkgroup &&
-                               ContainsMatrix(global->Type())) {
-                        // Due to a bug in the MSL compiler, we use a threadgroup memory
-                        // argument for any workgroup allocation that contains a matrix.
-                        // See crbug.com/tint/938.
-                        // TODO(jrprice): Do this for all other workgroup variables too.
-
-                        // Create a member in the workgroup parameter struct.
-                        auto member = ctx.Clone(var->symbol);
-                        workgroup_parameter_members.push_back(
-                            ctx.dst->Member(member, store_type()));
-                        CloneStructTypes(global->Type()->UnwrapRef());
-
-                        // Create a function-scope variable that is a pointer to the member.
-                        auto* member_ptr = ctx.dst->AddressOf(
-                            ctx.dst->MemberAccessor(ctx.dst->Deref(workgroup_param()), member));
-                        auto* local_var = ctx.dst->Let(
-                            new_var_symbol,
-                            ctx.dst->ty.pointer(store_type(), ast::StorageClass::kWorkgroup),
-                            member_ptr);
-                        ctx.InsertFront(func_ast->body->statements, ctx.dst->Decl(local_var));
-                        is_pointer = true;
-                    } else {
-                        // Variables in the Private and Workgroup storage classes are
-                        // redeclared at function scope. Disable storage class validation on
-                        // this variable.
-                        auto* disable_validation =
-                            ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass);
-                        auto* constructor = ctx.Clone(var->constructor);
-                        auto* local_var =
-                            ctx.dst->Var(new_var_symbol, store_type(), sc, constructor,
-                                         ast::AttributeList{disable_validation});
-                        ctx.InsertFront(func_ast->body->statements, ctx.dst->Decl(local_var));
                     }
+                }
+
+                if (local_private) {
+                    // Redeclare the variable at function scope.
+                    auto* disable_validation =
+                        ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass);
+                    auto* constructor = ctx.Clone(var->Declaration()->constructor);
+                    auto* local_var = ctx.dst->Var(new_var_symbol,
+                                                   CreateASTTypeFor(ctx, var->Type()->UnwrapRef()),
+                                                   ast::StorageClass::kPrivate, constructor,
+                                                   ast::AttributeList{disable_validation});
+                    ctx.InsertFront(func_ast->body->statements, ctx.dst->Decl(local_var));
+                    local_private_vars_.insert(var);
                 } else {
-                    // For a regular function, redeclare the variable as a parameter.
-                    // Use a pointer for non-handle types.
-                    auto* param_type = store_type();
-                    ast::AttributeList attributes;
-                    if (!global->Type()->UnwrapRef()->is_handle()) {
-                        param_type = ctx.dst->ty.pointer(param_type, sc, var->declared_access);
-                        is_pointer = true;
-
-                        // Disable validation of the parameter's storage class and of
-                        // arguments passed it.
-                        attributes.push_back(
-                            ctx.dst->Disable(ast::DisabledValidation::kIgnoreStorageClass));
-                        attributes.push_back(ctx.dst->Disable(
-                            ast::DisabledValidation::kIgnoreInvalidPointerArgument));
+                    // Process the variable to redeclare it as a parameter or local variable.
+                    if (is_entry_point) {
+                        ProcessVariableInEntryPoint(func_ast, var, new_var_symbol, workgroup_param,
+                                                    workgroup_parameter_members, is_pointer,
+                                                    is_wrapped);
+                    } else {
+                        ProcessVariableInUserFunction(func_ast, var, new_var_symbol, is_pointer);
                     }
-                    ctx.InsertBack(func_ast->params,
-                                   ctx.dst->Param(new_var_symbol, param_type, attributes));
+
+                    // Record the replacement symbol.
+                    var_to_newvar[var] = {new_var_symbol, is_pointer, is_wrapped};
                 }
 
                 // Replace all uses of the module-scope variable.
-                // For non-entry points, dereference non-handle pointer parameters.
-                for (auto* user : global->Users()) {
-                    if (user->Stmt()->Function()->Declaration() == func_ast) {
-                        const ast::Expression* expr = ctx.dst->Expr(new_var_symbol);
-                        if (is_pointer) {
-                            // If this identifier is used by an address-of operator, just
-                            // remove the address-of instead of adding a deref, since we
-                            // already have a pointer.
-                            auto* ident = user->Declaration()->As<ast::IdentifierExpression>();
-                            if (ident_to_address_of.count(ident)) {
-                                ctx.Replace(ident_to_address_of[ident], expr);
-                                continue;
-                            }
-
-                            expr = ctx.dst->Deref(expr);
-                        }
-                        if (is_wrapped) {
-                            // Get the member from the wrapper structure.
-                            expr = ctx.dst->MemberAccessor(expr, kWrappedArrayMemberName);
-                        }
-                        ctx.Replace(user->Declaration(), expr);
-                    }
-                }
-
-                var_to_newvar[global] = {new_var_symbol, is_pointer, is_wrapped};
+                ReplaceUsesInFunction(func_ast, var, new_var_symbol, is_pointer, is_wrapped);
             }
 
             if (!workgroup_parameter_members.empty()) {
                 // Create the workgroup memory parameter.
-                // The parameter is a struct that contains members for each workgroup
-                // variable.
+                // The parameter is a struct that contains members for each workgroup variable.
                 auto* str =
                     ctx.dst->Structure(ctx.dst->Sym(), std::move(workgroup_parameter_members));
                 auto* param_type =
@@ -331,12 +433,18 @@
                         continue;
                     }
 
-                    auto new_var = var_to_newvar[target_var];
+                    auto it = var_to_newvar.find(target_var);
+                    if (it == var_to_newvar.end()) {
+                        // No replacement was created for this function.
+                        continue;
+                    }
+
+                    auto new_var = it->second;
                     bool is_handle = target_var->Type()->UnwrapRef()->is_handle();
                     const ast::Expression* arg = ctx.dst->Expr(new_var.symbol);
                     if (new_var.is_wrapped) {
-                        // The variable is wrapped in a struct, so we need to pass a pointer
-                        // to the struct member instead.
+                        // The variable is wrapped in a struct, so we need to pass a pointer to the
+                        // struct member instead.
                         arg = ctx.dst->AddressOf(
                             ctx.dst->MemberAccessor(ctx.dst->Deref(arg), kWrappedArrayMemberName));
                     } else if (is_entry_point && !is_handle && !new_var.is_pointer) {
@@ -359,7 +467,15 @@
     }
 
   private:
+    // The structures that have already been cloned by this transform.
     std::unordered_set<const sem::Struct*> cloned_structs_;
+
+    // Set of a private variables that are local to a single function.
+    std::unordered_set<const sem::Variable*> local_private_vars_;
+
+    // Map from identifier expression to the address-of expression that uses it.
+    std::unordered_map<const ast::IdentifierExpression*, const ast::UnaryOpExpression*>
+        ident_to_address_of_;
 };
 
 ModuleScopeVarToEntryPointParam::ModuleScopeVarToEntryPointParam() = default;
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
index 9e81d31..dcf8912 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
@@ -95,9 +95,14 @@
 fn no_uses() {
 }
 
+fn zoo() {
+  p = p * 2.0;
+}
+
 fn bar(a : f32, b : f32) {
   p = a;
   w = b;
+  zoo();
 }
 
 fn foo(a : f32) {
@@ -116,22 +121,27 @@
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<workgroup, f32>) {
-  *(tint_symbol) = a;
-  *(tint_symbol_1) = b;
+fn zoo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
+  *(tint_symbol) = (*(tint_symbol) * 2.0);
 }
 
-fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<workgroup, f32>) {
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
+  *(tint_symbol_1) = a;
+  *(tint_symbol_2) = b;
+  zoo(tint_symbol_1);
+}
+
+fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
   let b : f32 = 2.0;
-  bar(a, b, tint_symbol_2, tint_symbol_3);
+  bar(a, b, tint_symbol_3, tint_symbol_4);
   no_uses();
 }
 
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_4 : f32;
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_5 : f32;
-  foo(1.0, &(tint_symbol_4), &(tint_symbol_5));
+  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_5 : f32;
+  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_6 : f32;
+  foo(1.0, &(tint_symbol_5), &(tint_symbol_6));
 }
 )";
 
@@ -159,6 +169,11 @@
 fn bar(a : f32, b : f32) {
   p = a;
   w = b;
+  zoo();
+}
+
+fn zoo() {
+  p = p * 2.0;
 }
 
 var<private> p : f32;
@@ -168,23 +183,28 @@
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
-  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
-  foo(1.0, &(tint_symbol), &(tint_symbol_1));
+  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_5 : f32;
+  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_6 : f32;
+  foo(1.0, &(tint_symbol_5), &(tint_symbol_6));
 }
 
-fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<workgroup, f32>) {
+fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<workgroup, f32>) {
   let b : f32 = 2.0;
-  bar(a, b, tint_symbol_2, tint_symbol_3);
+  bar(a, b, tint_symbol_3, tint_symbol_4);
   no_uses();
 }
 
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_5 : ptr<workgroup, f32>) {
-  *(tint_symbol_4) = a;
-  *(tint_symbol_5) = b;
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<workgroup, f32>) {
+  *(tint_symbol_1) = a;
+  *(tint_symbol_2) = b;
+  zoo(tint_symbol_1);
+}
+
+fn zoo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
+  *(tint_symbol) = (*(tint_symbol) * 2.0);
 }
 )";
 
@@ -307,9 +327,9 @@
 
 TEST_F(ModuleScopeVarToEntryPointParamTest, FoldAddressOfDeref) {
     auto* src = R"(
-var<private> v : f32;
+var<workgroup> v : f32;
 
-fn bar(p : ptr<private, f32>) {
+fn bar(p : ptr<workgroup, f32>) {
   (*p) = 0.0;
 }
 
@@ -324,17 +344,17 @@
 )";
 
     auto* expect = R"(
-fn bar(p : ptr<private, f32>) {
+fn bar(p : ptr<workgroup, f32>) {
   *(p) = 0.0;
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<private, f32>) {
+fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
   bar(tint_symbol);
 }
 
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol_1 : f32;
+  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
   foo(&(tint_symbol_1));
 }
 )";
@@ -355,25 +375,25 @@
   bar(&v);
 }
 
-fn bar(p : ptr<private, f32>) {
+fn bar(p : ptr<workgroup, f32>) {
   (*p) = 0.0;
 }
 
-var<private> v : f32;
+var<workgroup> v : f32;
 )";
 
     auto* expect = R"(
 @compute @workgroup_size(1)
 fn main() {
-  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
-  foo(&(tint_symbol));
+  @internal(disable_validation__ignore_storage_class) var<workgroup> tint_symbol_1 : f32;
+  foo(&(tint_symbol_1));
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<private, f32>) {
-  bar(tint_symbol_1);
+fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<workgroup, f32>) {
+  bar(tint_symbol);
 }
 
-fn bar(p : ptr<private, f32>) {
+fn bar(p : ptr<workgroup, f32>) {
   *(p) = 0.0;
 }
 )";
@@ -556,17 +576,17 @@
 )";
 
     auto* expect = R"(
-struct tint_symbol_1 {
+struct tint_symbol_2 {
   arr : array<f32>,
 }
 
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<storage, tint_symbol_1>) {
-  foo(&((*(tint_symbol)).arr));
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, tint_symbol_2>) {
+  foo(&((*(tint_symbol_1)).arr));
 }
 
-fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<storage, array<f32>>) {
-  _ = (*(tint_symbol_2))[0];
+fn foo(@internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<storage, array<f32>>) {
+  _ = (*(tint_symbol))[0];
 }
 )";
 
@@ -802,8 +822,8 @@
 
     auto* expect = R"(
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_1 : ptr<storage, S>) {
-  foo(1.0, tint_symbol, tint_symbol_1);
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_4 : ptr<uniform, S>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_storage_class) tint_symbol_5 : ptr<storage, S>) {
+  foo(1.0, tint_symbol_4, tint_symbol_5);
 }
 
 fn foo(a : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_2 : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_3 : ptr<storage, S>) {
@@ -816,9 +836,9 @@
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_4 : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_5 : ptr<storage, S>) {
-  _ = *(tint_symbol_4);
-  _ = *(tint_symbol_5);
+fn bar(a : f32, b : f32, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol : ptr<uniform, S>, @internal(disable_validation__ignore_storage_class) @internal(disable_validation__ignore_invalid_pointer_argument) tint_symbol_1 : ptr<storage, S>) {
+  _ = *(tint_symbol);
+  _ = *(tint_symbol_1);
 }
 
 struct S {
@@ -937,8 +957,8 @@
 
     auto* expect = R"(
 @compute @workgroup_size(1)
-fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) tint_symbol : texture_2d<f32>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) tint_symbol_1 : sampler) {
-  foo(1.0, tint_symbol, tint_symbol_1);
+fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) tint_symbol_4 : texture_2d<f32>, @group(0) @binding(1) @internal(disable_validation__entry_point_parameter) tint_symbol_5 : sampler) {
+  foo(1.0, tint_symbol_4, tint_symbol_5);
 }
 
 fn foo(a : f32, tint_symbol_2 : texture_2d<f32>, tint_symbol_3 : sampler) {
@@ -951,9 +971,9 @@
 fn no_uses() {
 }
 
-fn bar(a : f32, b : f32, tint_symbol_4 : texture_2d<f32>, tint_symbol_5 : sampler) {
-  _ = tint_symbol_4;
-  _ = tint_symbol_5;
+fn bar(a : f32, b : f32, tint_symbol : texture_2d<f32>, tint_symbol_1 : sampler) {
+  _ = tint_symbol;
+  _ = tint_symbol_1;
 }
 )";
 
@@ -1152,6 +1172,81 @@
     EXPECT_EQ(expect, str(got));
 }
 
+// Test that a private variable that is only referenced by a single user-defined function is
+// promoted to a function scope variable, rather than passed as a parameter.
+TEST_F(ModuleScopeVarToEntryPointParamTest, PromotePrivateToFunctionScope) {
+    auto* src = R"(
+var<private> p : f32;
+
+fn foo(a : f32) -> f32 {
+  let x = p;
+  p = x * a;
+  return p;
+}
+
+@compute @workgroup_size(1)
+fn main() {
+  _ = foo(1.0);
+}
+)";
+
+    auto* expect = R"(
+fn foo(a : f32) -> f32 {
+  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+  let x = tint_symbol;
+  tint_symbol = (x * a);
+  return tint_symbol;
+}
+
+@compute @workgroup_size(1)
+fn main() {
+  _ = foo(1.0);
+}
+)";
+
+    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// Test that a private variable that is only referenced by a single user-defined function is
+// promoted to a function scope variable, rather than passed as a parameter.
+TEST_F(ModuleScopeVarToEntryPointParamTest, PromotePrivateToFunctionScope_OutOfOrder) {
+    auto* src = R"(
+var<private> p : f32;
+
+@compute @workgroup_size(1)
+fn main() {
+  _ = foo(1.0);
+}
+
+fn foo(a : f32) -> f32 {
+  let x = p;
+  p = x * a;
+  return p;
+}
+
+)";
+
+    auto* expect = R"(
+@compute @workgroup_size(1)
+fn main() {
+  _ = foo(1.0);
+}
+
+fn foo(a : f32) -> f32 {
+  @internal(disable_validation__ignore_storage_class) var<private> tint_symbol : f32;
+  let x = tint_symbol;
+  tint_symbol = (x * a);
+  return tint_symbol;
+}
+)";
+
+    auto got = Run<ModuleScopeVarToEntryPointParam>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
 TEST_F(ModuleScopeVarToEntryPointParamTest, EmtpyModule) {
     auto* src = "";
 
diff --git a/src/tint/transform/multiplanar_external_texture.cc b/src/tint/transform/multiplanar_external_texture.cc
index 8b85573..c3aba9b 100644
--- a/src/tint/transform/multiplanar_external_texture.cc
+++ b/src/tint/transform/multiplanar_external_texture.cc
@@ -131,12 +131,12 @@
             auto& syms = new_binding_symbols[sem_var];
             syms.plane_0 = ctx.Clone(global->symbol);
             syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
-            b.Global(syms.plane_1, b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
-                     b.GroupAndBinding(bps.plane_1.group, bps.plane_1.binding));
+            b.GlobalVar(syms.plane_1, b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
+                        b.GroupAndBinding(bps.plane_1.group, bps.plane_1.binding));
             syms.params = b.Symbols().New("ext_tex_params");
-            b.Global(syms.params, b.ty.type_name("ExternalTextureParams"),
-                     ast::StorageClass::kUniform,
-                     b.GroupAndBinding(bps.params.group, bps.params.binding));
+            b.GlobalVar(syms.params, b.ty.type_name("ExternalTextureParams"),
+                        ast::StorageClass::kUniform,
+                        b.GroupAndBinding(bps.params.group, bps.params.binding));
 
             // Replace the original texture_external binding with a texture_2d<f32>
             // binding.
@@ -251,6 +251,7 @@
         // Create ExternalTextureParams struct.
         ast::StructMemberList ext_tex_params_member_list = {
             b.Member("numPlanes", b.ty.u32()),
+            b.Member("doYuvToRgbConversionOnly", b.ty.u32()),
             b.Member("yuvToRgbConversionMatrix", b.ty.mat3x4(b.ty.f32())),
             b.Member("gammaDecodeParams", b.ty.type_name("GammaTransferParams")),
             b.Member("gammaEncodeParams", b.ty.type_name("GammaTransferParams")),
@@ -340,14 +341,20 @@
                               b.Mul(b.vec4<f32>(b.MemberAccessor(plane_0_call, "r"),
                                                 b.MemberAccessor(plane_1_call, "rg"), 1_f),
                                     b.MemberAccessor("params", "yuvToRgbConversionMatrix")))))),
-            // color = gammaConversion(color, gammaDecodeParams);
-            b.Assign("color", b.Call("gammaCorrection", "color",
-                                     b.MemberAccessor("params", "gammaDecodeParams"))),
-            // color = (params.gamutConversionMatrix * color);
-            b.Assign("color", b.Mul(b.MemberAccessor("params", "gamutConversionMatrix"), "color")),
-            // color = gammaConversion(color, gammaEncodeParams);
-            b.Assign("color", b.Call("gammaCorrection", "color",
-                                     b.MemberAccessor("params", "gammaEncodeParams"))),
+            // if (params.doYuvToRgbConversionOnly == 0u)
+            b.If(b.create<ast::BinaryExpression>(
+                     ast::BinaryOp::kEqual, b.MemberAccessor("params", "doYuvToRgbConversionOnly"),
+                     b.Expr(0_u)),
+                 b.Block(
+                     // color = gammaConversion(color, gammaDecodeParams);
+                     b.Assign("color", b.Call("gammaCorrection", "color",
+                                              b.MemberAccessor("params", "gammaDecodeParams"))),
+                     // color = (params.gamutConversionMatrix * color);
+                     b.Assign("color",
+                              b.Mul(b.MemberAccessor("params", "gamutConversionMatrix"), "color")),
+                     // color = gammaConversion(color, gammaEncodeParams);
+                     b.Assign("color", b.Call("gammaCorrection", "color",
+                                              b.MemberAccessor("params", "gammaEncodeParams"))))),
             // return vec4<f32>(color, 1.f);
             b.Return(b.vec4<f32>("color", 1_f))};
     }
diff --git a/src/tint/transform/multiplanar_external_texture.h b/src/tint/transform/multiplanar_external_texture.h
index 88cbc98..fcb5156 100644
--- a/src/tint/transform/multiplanar_external_texture.h
+++ b/src/tint/transform/multiplanar_external_texture.h
@@ -46,7 +46,10 @@
 /// textureSampleLevel that contain a texture_external parameter will be
 /// transformed into a newly generated version of the function, which can
 /// perform the desired operation on a single RGBA plane or on seperate Y and UV
-/// planes.
+/// planes, and do colorspace conversions including yuv->rgb conversion, gamma
+/// decoding, gamut conversion, and gamma encoding steps. Specifically
+// for BT.709 to SRGB conversion, it takes the fast path only doing the yuv->rgb
+// step and skipping all other steps.
 class MultiplanarExternalTexture : public Castable<MultiplanarExternalTexture, Transform> {
   public:
     /// BindingsMap is a map where the key is the binding location of a
diff --git a/src/tint/transform/multiplanar_external_texture_test.cc b/src/tint/transform/multiplanar_external_texture_test.cc
index 63d12f1..79f9fdd 100644
--- a/src/tint/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/transform/multiplanar_external_texture_test.cc
@@ -118,6 +118,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -172,6 +173,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -225,6 +227,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -253,9 +256,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -298,6 +303,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -322,9 +328,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -370,6 +378,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -396,9 +405,11 @@
   } else {
     color = (vec4<f32>(textureLoad(plane0, coord, 0i).r, textureLoad(plane1, coord, 0i).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -440,6 +451,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -464,9 +476,11 @@
   } else {
     color = (vec4<f32>(textureLoad(plane0, coord, 0i).r, textureLoad(plane1, coord, 0i).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -512,6 +526,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -540,9 +555,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -553,9 +570,11 @@
   } else {
     color = (vec4<f32>(textureLoad(plane0, coord, 0i).r, textureLoad(plane1, coord, 0i).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -599,6 +618,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -623,9 +643,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -636,9 +658,11 @@
   } else {
     color = (vec4<f32>(textureLoad(plane0, coord, 0i).r, textureLoad(plane1, coord, 0i).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -688,6 +712,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -734,9 +759,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -788,6 +815,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -812,9 +840,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -870,6 +900,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -899,9 +930,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -952,6 +985,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -976,9 +1010,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -1036,6 +1072,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1064,9 +1101,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -1129,6 +1168,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1162,9 +1202,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -1223,6 +1265,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1247,9 +1290,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -1313,6 +1358,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1337,9 +1383,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -1391,6 +1439,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1441,6 +1490,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1467,9 +1517,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
@@ -1526,6 +1578,7 @@
 
 struct ExternalTextureParams {
   numPlanes : u32,
+  doYuvToRgbConversionOnly : u32,
   yuvToRgbConversionMatrix : mat3x4<f32>,
   gammaDecodeParams : GammaTransferParams,
   gammaEncodeParams : GammaTransferParams,
@@ -1555,9 +1608,11 @@
   } else {
     color = (vec4<f32>(textureSampleLevel(plane0, smp, coord, 0.0f).r, textureSampleLevel(plane1, smp, coord, 0.0f).rg, 1.0f) * params.yuvToRgbConversionMatrix);
   }
-  color = gammaCorrection(color, params.gammaDecodeParams);
-  color = (params.gamutConversionMatrix * color);
-  color = gammaCorrection(color, params.gammaEncodeParams);
+  if ((params.doYuvToRgbConversionOnly == 0u)) {
+    color = gammaCorrection(color, params.gammaDecodeParams);
+    color = (params.gamutConversionMatrix * color);
+    color = gammaCorrection(color, params.gammaEncodeParams);
+  }
   return vec4<f32>(color, 1.0f);
 }
 
diff --git a/src/tint/transform/num_workgroups_from_uniform.cc b/src/tint/transform/num_workgroups_from_uniform.cc
index bba1580..6b93e73 100644
--- a/src/tint/transform/num_workgroups_from_uniform.cc
+++ b/src/tint/transform/num_workgroups_from_uniform.cc
@@ -144,7 +144,7 @@
                 binding = 0;
             }
 
-            num_workgroups_ubo = ctx.dst->Global(
+            num_workgroups_ubo = ctx.dst->GlobalVar(
                 ctx.dst->Sym(), ctx.dst->ty.Of(num_workgroups_struct), ast::StorageClass::kUniform,
                 ast::AttributeList{ctx.dst->GroupAndBinding(group, binding)});
         }
diff --git a/src/tint/transform/promote_initializers_to_const_var.cc b/src/tint/transform/promote_initializers_to_const_var.cc
deleted file mode 100644
index 6e0ba55..0000000
--- a/src/tint/transform/promote_initializers_to_const_var.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2022 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/transform/promote_initializers_to_const_var.h"
-#include "src/tint/program_builder.h"
-#include "src/tint/sem/call.h"
-#include "src/tint/sem/statement.h"
-#include "src/tint/sem/type_constructor.h"
-#include "src/tint/transform/utils/hoist_to_decl_before.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::transform::PromoteInitializersToConstVar);
-
-namespace tint::transform {
-
-PromoteInitializersToConstVar::PromoteInitializersToConstVar() = default;
-
-PromoteInitializersToConstVar::~PromoteInitializersToConstVar() = default;
-
-void PromoteInitializersToConstVar::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    HoistToDeclBefore hoist_to_decl_before(ctx);
-
-    // Hoists array and structure initializers to a constant variable, declared
-    // just before the statement of usage.
-    auto type_ctor_to_let = [&](const ast::CallExpression* expr) {
-        auto* ctor = ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::Call>();
-        if (!ctor->Target()->Is<sem::TypeConstructor>()) {
-            return true;
-        }
-        auto* sem_stmt = ctor->Stmt();
-        if (!sem_stmt) {
-            // Expression is outside of a statement. This usually means the
-            // expression is part of a global (module-scope) constant declaration.
-            // These must be constexpr, and so cannot contain the type of
-            // expressions that must be sanitized.
-            return true;
-        }
-
-        auto* stmt = sem_stmt->Declaration();
-
-        if (auto* src_var_decl = stmt->As<ast::VariableDeclStatement>()) {
-            if (src_var_decl->variable->constructor == expr) {
-                // This statement is just a variable declaration with the
-                // initializer as the constructor value. This is what we're
-                // attempting to transform to, and so ignore.
-                return true;
-            }
-        }
-
-        auto* src_ty = ctor->Type();
-        if (!src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
-            // We only care about array and struct initializers
-            return true;
-        }
-
-        return hoist_to_decl_before.Add(ctor, expr, true);
-    };
-
-    for (auto* node : ctx.src->ASTNodes().Objects()) {
-        if (auto* call_expr = node->As<ast::CallExpression>()) {
-            if (!type_ctor_to_let(call_expr)) {
-                return;
-            }
-        }
-    }
-
-    hoist_to_decl_before.Apply();
-    ctx.Clone();
-}
-
-}  // namespace tint::transform
diff --git a/src/tint/transform/promote_initializers_to_const_var_test.cc b/src/tint/transform/promote_initializers_to_const_var_test.cc
deleted file mode 100644
index f322478..0000000
--- a/src/tint/transform/promote_initializers_to_const_var_test.cc
+++ /dev/null
@@ -1,625 +0,0 @@
-// Copyright 2021 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/transform/promote_initializers_to_const_var.h"
-
-#include "src/tint/transform/test_helper.h"
-
-namespace tint::transform {
-namespace {
-
-using PromoteInitializersToConstVarTest = TransformTest;
-
-TEST_F(PromoteInitializersToConstVarTest, EmptyModule) {
-    auto* src = "";
-    auto* expect = "";
-
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, BasicArray) {
-    auto* src = R"(
-fn f() {
-  var f0 = 1.0;
-  var f1 = 2.0;
-  var f2 = 3.0;
-  var f3 = 4.0;
-  var i = array<f32, 4u>(f0, f1, f2, f3)[2];
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f0 = 1.0;
-  var f1 = 2.0;
-  var f2 = 3.0;
-  var f3 = 4.0;
-  let tint_symbol = array<f32, 4u>(f0, f1, f2, f3);
-  var i = tint_symbol[2];
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, BasicStruct) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-
-fn f() {
-  var x = S(1, 2.0, vec3<f32>()).b;
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-
-fn f() {
-  let tint_symbol = S(1, 2.0, vec3<f32>());
-  var x = tint_symbol.b;
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, BasicStruct_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var x = S(1, 2.0, vec3<f32>()).b;
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-)";
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol = S(1, 2.0, vec3<f32>());
-  var x = tint_symbol.b;
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopInit) {
-    auto* src = R"(
-fn f() {
-  var insert_after = 1;
-  for(var i = array<f32, 4u>(0.0, 1.0, 2.0, 3.0)[2]; ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var insert_after = 1;
-  let tint_symbol = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  for(var i = tint_symbol[2]; ; ) {
-    break;
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, StructInForLoopInit) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-
-fn f() {
-  var insert_after = 1;
-  for(var x = S(1, 2.0, vec3<f32>()).b; ; ) {
-    break;
-  }
-}
-)";
-
-    auto* expect = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-
-fn f() {
-  var insert_after = 1;
-  let tint_symbol = S(1, 2.0, vec3<f32>());
-  for(var x = tint_symbol.b; ; ) {
-    break;
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, StructInForLoopInit_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var insert_after = 1;
-  for(var x = S(1, 2.0, vec3<f32>()).b; ; ) {
-    break;
-  }
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-};
-)";
-
-    auto* expect = R"(
-fn f() {
-  var insert_after = 1;
-  let tint_symbol = S(1, 2.0, vec3<f32>());
-  for(var x = tint_symbol.b; ; ) {
-    break;
-  }
-}
-
-struct S {
-  a : i32,
-  b : f32,
-  c : vec3<f32>,
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopCond) {
-    auto* src = R"(
-fn f() {
-  var f = 1.0;
-  for(; f == array<f32, 1u>(f)[0]; f = f + 1.0) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 1.0;
-  loop {
-    let tint_symbol = array<f32, 1u>(f);
-    if (!((f == tint_symbol[0]))) {
-      break;
-    }
-    {
-      var marker = 1;
-    }
-
-    continuing {
-      f = (f + 1.0);
-    }
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopCont) {
-    auto* src = R"(
-fn f() {
-  var f = 0.0;
-  for(; f < 10.0; f = f + array<f32, 1u>(1.0)[0]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 0.0;
-  loop {
-    if (!((f < 10.0))) {
-      break;
-    }
-    {
-      var marker = 1;
-    }
-
-    continuing {
-      let tint_symbol = array<f32, 1u>(1.0);
-      f = (f + tint_symbol[0]);
-    }
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInForLoopInitCondCont) {
-    auto* src = R"(
-fn f() {
-  for(var f = array<f32, 1u>(0.0)[0];
-      f < array<f32, 1u>(1.0)[0];
-      f = f + array<f32, 1u>(2.0)[0]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol = array<f32, 1u>(0.0);
-  {
-    var f = tint_symbol[0];
-    loop {
-      let tint_symbol_1 = array<f32, 1u>(1.0);
-      if (!((f < tint_symbol_1[0]))) {
-        break;
-      }
-      {
-        var marker = 1;
-      }
-
-      continuing {
-        let tint_symbol_2 = array<f32, 1u>(2.0);
-        f = (f + tint_symbol_2[0]);
-      }
-    }
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInElseIf) {
-    auto* src = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else if (f == array<f32, 2u>(f, f)[0]) {
-    var marker = 1;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else {
-    let tint_symbol = array<f32, 2u>(f, f);
-    if ((f == tint_symbol[0])) {
-      var marker = 1;
-    }
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInElseIfChain) {
-    auto* src = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else if (f == array<f32, 2u>(f, f)[0]) {
-    var marker = 2;
-  } else if (f == array<f32, 2u>(f, f)[1]) {
-    var marker = 3;
-  } else if (true) {
-    var marker = 4;
-  } else {
-    var marker = 5;
-  }
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  var f = 1.0;
-  if (true) {
-    var marker = 0;
-  } else if (true) {
-    var marker = 1;
-  } else {
-    let tint_symbol = array<f32, 2u>(f, f);
-    if ((f == tint_symbol[0])) {
-      var marker = 2;
-    } else {
-      let tint_symbol_1 = array<f32, 2u>(f, f);
-      if ((f == tint_symbol_1[1])) {
-        var marker = 3;
-      } else if (true) {
-        var marker = 4;
-      } else {
-        var marker = 5;
-      }
-    }
-  }
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, ArrayInArrayArray) {
-    auto* src = R"(
-fn f() {
-  var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[0][1];
-}
-)";
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol = array<f32, 2u>(1.0, 2.0);
-  let tint_symbol_1 = array<f32, 2u>(3.0, 4.0);
-  let tint_symbol_2 = array<array<f32, 2u>, 2u>(tint_symbol, tint_symbol_1);
-  var i = tint_symbol_2[0][1];
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, StructNested) {
-    auto* src = R"(
-struct S1 {
-  a : i32,
-};
-
-struct S2 {
-  a : i32,
-  b : S1,
-  c : i32,
-};
-
-struct S3 {
-  a : S2,
-};
-
-fn f() {
-  var x = S3(S2(1, S1(2), 3)).a.b.a;
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-}
-
-struct S2 {
-  a : i32,
-  b : S1,
-  c : i32,
-}
-
-struct S3 {
-  a : S2,
-}
-
-fn f() {
-  let tint_symbol = S1(2);
-  let tint_symbol_1 = S2(1, tint_symbol, 3);
-  let tint_symbol_2 = S3(tint_symbol_1);
-  var x = tint_symbol_2.a.b.a;
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, Mixed) {
-    auto* src = R"(
-struct S1 {
-  a : i32,
-};
-
-struct S2 {
-  a : array<S1, 3u>,
-};
-
-fn f() {
-  var x = S2(array<S1, 3u>(S1(1), S1(2), S1(3))).a[1].a;
-}
-)";
-
-    auto* expect = R"(
-struct S1 {
-  a : i32,
-}
-
-struct S2 {
-  a : array<S1, 3u>,
-}
-
-fn f() {
-  let tint_symbol = S1(1);
-  let tint_symbol_1 = S1(2);
-  let tint_symbol_2 = S1(3);
-  let tint_symbol_3 = array<S1, 3u>(tint_symbol, tint_symbol_1, tint_symbol_2);
-  let tint_symbol_4 = S2(tint_symbol_3);
-  var x = tint_symbol_4.a[1].a;
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, Mixed_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var x = S2(array<S1, 3u>(S1(1), S1(2), S1(3))).a[1].a;
-}
-
-struct S2 {
-  a : array<S1, 3u>,
-};
-
-struct S1 {
-  a : i32,
-};
-)";
-
-    auto* expect = R"(
-fn f() {
-  let tint_symbol = S1(1);
-  let tint_symbol_1 = S1(2);
-  let tint_symbol_2 = S1(3);
-  let tint_symbol_3 = array<S1, 3u>(tint_symbol, tint_symbol_1, tint_symbol_2);
-  let tint_symbol_4 = S2(tint_symbol_3);
-  var x = tint_symbol_4.a[1].a;
-}
-
-struct S2 {
-  a : array<S1, 3u>,
-}
-
-struct S1 {
-  a : i32,
-}
-)";
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl) {
-    auto* src = R"(
-struct S {
-  a : i32,
-  b : f32,
-  c : i32,
-}
-
-fn f() {
-  var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  var local_str = S(1, 2.0, 3);
-}
-
-let module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-
-let module_str : S = S(1, 2.0, 3);
-)";
-
-    auto* expect = src;
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-  var local_str = S(1, 2.0, 3);
-}
-
-let module_str : S = S(1, 2.0, 3);
-
-struct S {
-  a : i32,
-  b : f32,
-  c : i32,
-}
-
-let module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
-)";
-
-    auto* expect = src;
-
-    DataMap data;
-    auto got = Run<PromoteInitializersToConstVar>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::transform
diff --git a/src/tint/transform/promote_initializers_to_let.cc b/src/tint/transform/promote_initializers_to_let.cc
new file mode 100644
index 0000000..b57fb25
--- /dev/null
+++ b/src/tint/transform/promote_initializers_to_let.cc
@@ -0,0 +1,106 @@
+// Copyright 2022 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/transform/promote_initializers_to_let.h"
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/call.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/sem/type_constructor.h"
+#include "src/tint/transform/utils/hoist_to_decl_before.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::PromoteInitializersToLet);
+
+namespace tint::transform {
+
+PromoteInitializersToLet::PromoteInitializersToLet() = default;
+
+PromoteInitializersToLet::~PromoteInitializersToLet() = default;
+
+void PromoteInitializersToLet::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+    HoistToDeclBefore hoist_to_decl_before(ctx);
+
+    // Hoists array and structure initializers to a constant variable, declared
+    // just before the statement of usage.
+    auto promote = [&](const sem::Expression* expr) {
+        auto* sem_stmt = expr->Stmt();
+        if (!sem_stmt) {
+            // Expression is outside of a statement. This usually means the
+            // expression is part of a global (module-scope) constant declaration.
+            // These must be constexpr, and so cannot contain the type of
+            // expressions that must be sanitized.
+            return true;
+        }
+
+        auto* stmt = sem_stmt->Declaration();
+
+        if (auto* src_var_decl = stmt->As<ast::VariableDeclStatement>()) {
+            if (src_var_decl->variable->constructor == expr->Declaration()) {
+                // This statement is just a variable declaration with the
+                // initializer as the constructor value. This is what we're
+                // attempting to transform to, and so ignore.
+                return true;
+            }
+        }
+
+        auto* src_ty = expr->Type();
+        if (!src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
+            // We only care about array and struct initializers
+            return true;
+        }
+
+        return hoist_to_decl_before.Add(expr, expr->Declaration(), true);
+    };
+
+    for (auto* node : ctx.src->ASTNodes().Objects()) {
+        bool ok = Switch(
+            node,  //
+            [&](const ast::CallExpression* expr) {
+                if (auto* sem = ctx.src->Sem().Get(expr)) {
+                    auto* ctor = sem->UnwrapMaterialize()->As<sem::Call>();
+                    if (ctor->Target()->Is<sem::TypeConstructor>()) {
+                        return promote(sem);
+                    }
+                }
+                return true;
+            },
+            [&](const ast::IdentifierExpression* expr) {
+                if (auto* user = ctx.src->Sem().Get<sem::VariableUser>(expr)) {
+                    // Identifier resolves to a variable
+                    if (auto* stmt = user->Stmt()) {
+                        if (auto* decl = stmt->Declaration()->As<ast::VariableDeclStatement>();
+                            decl && decl->variable->Is<ast::Const>()) {
+                            // The identifier is used on the RHS of a 'const' declaration. Ignore.
+                            return true;
+                        }
+                    }
+                    if (user->Variable()->Declaration()->Is<ast::Const>()) {
+                        // The identifier resolves to a 'const' variable, but isn't used to
+                        // initialize another 'const'. This needs promoting.
+                        return promote(user);
+                    }
+                }
+                return true;
+            },
+            [&](Default) { return true; });
+
+        if (!ok) {
+            return;
+        }
+    }
+
+    hoist_to_decl_before.Apply();
+    ctx.Clone();
+}
+
+}  // namespace tint::transform
diff --git a/src/tint/transform/promote_initializers_to_const_var.h b/src/tint/transform/promote_initializers_to_let.h
similarity index 63%
rename from src/tint/transform/promote_initializers_to_const_var.h
rename to src/tint/transform/promote_initializers_to_let.h
index 67a32c4..41f99d7 100644
--- a/src/tint/transform/promote_initializers_to_const_var.h
+++ b/src/tint/transform/promote_initializers_to_let.h
@@ -12,23 +12,26 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SRC_TINT_TRANSFORM_PROMOTE_INITIALIZERS_TO_CONST_VAR_H_
-#define SRC_TINT_TRANSFORM_PROMOTE_INITIALIZERS_TO_CONST_VAR_H_
+#ifndef SRC_TINT_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
+#define SRC_TINT_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
 
 #include "src/tint/transform/transform.h"
 
 namespace tint::transform {
 
-/// A transform that hoists the array and structure initializers to a constant
-/// variable, declared just before the statement of usage.
+/// A transform that hoists array and structure constructors, and identifiers resolving to a
+/// 'const' array to a 'let' variable, declared just before the statement of usage.
+/// This transform is used by backends that do not support expressions that operate on an immediate
+/// array or structure. For example, the following is not immediately expressable for HLSL:
+///   `array<i32, 2>(1, 2)[0]`
 /// @see crbug.com/tint/406
-class PromoteInitializersToConstVar : public Castable<PromoteInitializersToConstVar, Transform> {
+class PromoteInitializersToLet : public Castable<PromoteInitializersToLet, Transform> {
   public:
     /// Constructor
-    PromoteInitializersToConstVar();
+    PromoteInitializersToLet();
 
     /// Destructor
-    ~PromoteInitializersToConstVar() override;
+    ~PromoteInitializersToLet() override;
 
   protected:
     /// Runs the transform using the CloneContext built for transforming a
@@ -42,4 +45,4 @@
 
 }  // namespace tint::transform
 
-#endif  // SRC_TINT_TRANSFORM_PROMOTE_INITIALIZERS_TO_CONST_VAR_H_
+#endif  // SRC_TINT_TRANSFORM_PROMOTE_INITIALIZERS_TO_LET_H_
diff --git a/src/tint/transform/promote_initializers_to_let_test.cc b/src/tint/transform/promote_initializers_to_let_test.cc
new file mode 100644
index 0000000..d4d9fb2
--- /dev/null
+++ b/src/tint/transform/promote_initializers_to_let_test.cc
@@ -0,0 +1,1225 @@
+// Copyright 2021 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/transform/promote_initializers_to_let.h"
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using PromoteInitializersToLetTest = TransformTest;
+
+TEST_F(PromoteInitializersToLetTest, EmptyModule) {
+    auto* src = "";
+    auto* expect = "";
+
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, BasicArray) {
+    auto* src = R"(
+fn f() {
+  var f0 = 1.0;
+  var f1 = 2.0;
+  var f2 = 3.0;
+  var f3 = 4.0;
+  var i = array<f32, 4u>(f0, f1, f2, f3)[2];
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var f0 = 1.0;
+  var f1 = 2.0;
+  var f2 = 3.0;
+  var f3 = 4.0;
+  let tint_symbol = array<f32, 4u>(f0, f1, f2, f3);
+  var i = tint_symbol[2];
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, BasicStruct) {
+    auto* src = R"(
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+};
+
+fn f() {
+  var x = S(1, 2.0, vec3<f32>()).b;
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+}
+
+fn f() {
+  let tint_symbol = S(1, 2.0, vec3<f32>());
+  var x = tint_symbol.b;
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, BasicStruct_OutOfOrder) {
+    auto* src = R"(
+fn f() {
+  var x = S(1, 2.0, vec3<f32>()).b;
+}
+
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+};
+)";
+
+    auto* expect = R"(
+fn f() {
+  let tint_symbol = S(1, 2.0, vec3<f32>());
+  var x = tint_symbol.b;
+}
+
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray) {
+    auto* src = R"(
+const f0 = 1.0;
+
+const f1 = 2.0;
+
+const C = array<f32, 2u>(f0, f1);
+
+fn f() {
+  var f0 = 100.0;
+  var f1 = 100.0;
+  var i = C[1];
+}
+)";
+
+    auto* expect = R"(
+const f0 = 1.0;
+
+const f1 = 2.0;
+
+const C = array<f32, 2u>(f0, f1);
+
+fn f() {
+  var f0 = 100.0;
+  var f1 = 100.0;
+  let tint_symbol = C;
+  var i = tint_symbol[1];
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray_OutOfOrder) {
+    auto* src = R"(
+fn f() {
+  var f0 = 100.0;
+  var f1 = 100.0;
+  var i = C[1];
+}
+
+const C = array<f32, 2u>(f0, f1);
+
+const f0 = 1.0;
+
+const f1 = 2.0;
+)";
+
+    auto* expect = R"(
+fn f() {
+  var f0 = 100.0;
+  var f1 = 100.0;
+  let tint_symbol = C;
+  var i = tint_symbol[1];
+}
+
+const C = array<f32, 2u>(f0, f1);
+
+const f0 = 1.0;
+
+const f1 = 2.0;
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstBasicArray) {
+    auto* src = R"(
+fn f() {
+  const f0 = 1.0;
+  const f1 = 2.0;
+  const C = array<f32, 2u>(f0, f1);
+  var i = C[1];
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const f0 = 1.0;
+  const f1 = 2.0;
+  const C = array<f32, 2u>(f0, f1);
+  let tint_symbol = C;
+  var i = tint_symbol[1];
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInit) {
+    auto* src = R"(
+fn f() {
+  var insert_after = 1;
+  for(var i = array<f32, 4u>(0.0, 1.0, 2.0, 3.0)[2]; ; ) {
+    break;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var insert_after = 1;
+  let tint_symbol = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+  for(var i = tint_symbol[2]; ; ) {
+    break;
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInit) {
+    auto* src = R"(
+fn f() {
+  const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+  var insert_after = 1;
+  for(var i = arr[2]; ; ) {
+    break;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+  var insert_after = 1;
+  let tint_symbol = arr;
+  for(var i = tint_symbol[2]; ; ) {
+    break;
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopInit) {
+    auto* src = R"(
+const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+
+fn f() {
+  var insert_after = 1;
+  for(var i = arr[2]; ; ) {
+    break;
+  }
+}
+)";
+
+    auto* expect = R"(
+const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+
+fn f() {
+  var insert_after = 1;
+  let tint_symbol = arr;
+  for(var i = tint_symbol[2]; ; ) {
+    break;
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, StructInForLoopInit) {
+    auto* src = R"(
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+};
+
+fn f() {
+  var insert_after = 1;
+  for(var x = S(1, 2.0, vec3<f32>()).b; ; ) {
+    break;
+  }
+}
+)";
+
+    auto* expect = R"(
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+}
+
+fn f() {
+  var insert_after = 1;
+  let tint_symbol = S(1, 2.0, vec3<f32>());
+  for(var x = tint_symbol.b; ; ) {
+    break;
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, StructInForLoopInit_OutOfOrder) {
+    auto* src = R"(
+fn f() {
+  var insert_after = 1;
+  for(var x = S(1, 2.0, vec3<f32>()).b; ; ) {
+    break;
+  }
+}
+
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+};
+)";
+
+    auto* expect = R"(
+fn f() {
+  var insert_after = 1;
+  let tint_symbol = S(1, 2.0, vec3<f32>());
+  for(var x = tint_symbol.b; ; ) {
+    break;
+  }
+}
+
+struct S {
+  a : i32,
+  b : f32,
+  c : vec3<f32>,
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCond) {
+    auto* src = R"(
+fn f() {
+  var f = 1.0;
+  for(; f == array<f32, 1u>(f)[0]; f = f + 1.0) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var f = 1.0;
+  loop {
+    let tint_symbol = array<f32, 1u>(f);
+    if (!((f == tint_symbol[0]))) {
+      break;
+    }
+    {
+      var marker = 1;
+    }
+
+    continuing {
+      f = (f + 1.0);
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCond) {
+    auto* src = R"(
+fn f() {
+  const f = 1.0;
+  const arr = array<f32, 1u>(f);
+  for(var i = f; i == arr[0]; i = i + 1.0) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const f = 1.0;
+  const arr = array<f32, 1u>(f);
+  {
+    var i = f;
+    loop {
+      let tint_symbol = arr;
+      if (!((i == tint_symbol[0]))) {
+        break;
+      }
+      {
+        var marker = 1;
+      }
+
+      continuing {
+        i = (i + 1.0);
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCond) {
+    auto* src = R"(
+const f = 1.0;
+
+const arr = array<f32, 1u>(f);
+
+fn F() {
+  for(var i = f; i == arr[0]; i = i + 1.0) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+const f = 1.0;
+
+const arr = array<f32, 1u>(f);
+
+fn F() {
+  {
+    var i = f;
+    loop {
+      let tint_symbol = arr;
+      if (!((i == tint_symbol[0]))) {
+        break;
+      }
+      {
+        var marker = 1;
+      }
+
+      continuing {
+        i = (i + 1.0);
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCont) {
+    auto* src = R"(
+fn f() {
+  var f = 0.0;
+  for(; f < 10.0; f = f + array<f32, 1u>(1.0)[0]) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var f = 0.0;
+  loop {
+    if (!((f < 10.0))) {
+      break;
+    }
+    {
+      var marker = 1;
+    }
+
+    continuing {
+      let tint_symbol = array<f32, 1u>(1.0);
+      f = (f + tint_symbol[0]);
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCont) {
+    auto* src = R"(
+fn f() {
+  const arr = array<f32, 1u>(1.0);
+  var f = 0.0;
+  for(; f < 10.0; f = f + arr[0]) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const arr = array<f32, 1u>(1.0);
+  var f = 0.0;
+  loop {
+    if (!((f < 10.0))) {
+      break;
+    }
+    {
+      var marker = 1;
+    }
+
+    continuing {
+      let tint_symbol = arr;
+      f = (f + tint_symbol[0]);
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCont) {
+    auto* src = R"(
+const arr = array<f32, 1u>(1.0);
+
+fn f() {
+  var f = 0.0;
+  for(; f < 10.0; f = f + arr[0]) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+const arr = array<f32, 1u>(1.0);
+
+fn f() {
+  var f = 0.0;
+  loop {
+    if (!((f < 10.0))) {
+      break;
+    }
+    {
+      var marker = 1;
+    }
+
+    continuing {
+      let tint_symbol = arr;
+      f = (f + tint_symbol[0]);
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInitCondCont) {
+    auto* src = R"(
+fn f() {
+  for(var f = array<f32, 1u>(0.0)[0];
+      f < array<f32, 1u>(1.0)[0];
+      f = f + array<f32, 1u>(2.0)[0]) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let tint_symbol = array<f32, 1u>(0.0);
+  {
+    var f = tint_symbol[0];
+    loop {
+      let tint_symbol_1 = array<f32, 1u>(1.0);
+      if (!((f < tint_symbol_1[0]))) {
+        break;
+      }
+      {
+        var marker = 1;
+      }
+
+      continuing {
+        let tint_symbol_2 = array<f32, 1u>(2.0);
+        f = (f + tint_symbol_2[0]);
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInitCondCont) {
+    auto* src = R"(
+fn f() {
+  const arr_a = array<f32, 1u>(0.0);
+  const arr_b = array<f32, 1u>(1.0);
+  const arr_c = array<f32, 1u>(2.0);
+  for(var f = arr_a[0]; f < arr_b[0]; f = f + arr_c[0]) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const arr_a = array<f32, 1u>(0.0);
+  const arr_b = array<f32, 1u>(1.0);
+  const arr_c = array<f32, 1u>(2.0);
+  let tint_symbol = arr_a;
+  {
+    var f = tint_symbol[0];
+    loop {
+      let tint_symbol_1 = arr_b;
+      if (!((f < tint_symbol_1[0]))) {
+        break;
+      }
+      {
+        var marker = 1;
+      }
+
+      continuing {
+        let tint_symbol_2 = arr_c;
+        f = (f + tint_symbol_2[0]);
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInElseIf) {
+    auto* src = R"(
+fn f() {
+  var f = 1.0;
+  if (true) {
+    var marker = 0;
+  } else if (f == array<f32, 2u>(f, f)[0]) {
+    var marker = 1;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var f = 1.0;
+  if (true) {
+    var marker = 0;
+  } else {
+    let tint_symbol = array<f32, 2u>(f, f);
+    if ((f == tint_symbol[0])) {
+      var marker = 1;
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInElseIfChain) {
+    auto* src = R"(
+fn f() {
+  var f = 1.0;
+  if (true) {
+    var marker = 0;
+  } else if (true) {
+    var marker = 1;
+  } else if (f == array<f32, 2u>(f, f)[0]) {
+    var marker = 2;
+  } else if (f == array<f32, 2u>(f, f)[1]) {
+    var marker = 3;
+  } else if (true) {
+    var marker = 4;
+  } else {
+    var marker = 5;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  var f = 1.0;
+  if (true) {
+    var marker = 0;
+  } else if (true) {
+    var marker = 1;
+  } else {
+    let tint_symbol = array<f32, 2u>(f, f);
+    if ((f == tint_symbol[0])) {
+      var marker = 2;
+    } else {
+      let tint_symbol_1 = array<f32, 2u>(f, f);
+      if ((f == tint_symbol_1[1])) {
+        var marker = 3;
+      } else if (true) {
+        var marker = 4;
+      } else {
+        var marker = 5;
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstArrayInElseIfChain) {
+    auto* src = R"(
+fn f() {
+  const f = 1.0;
+  const arr = array<f32, 2u>(f, f);
+  if (true) {
+    var marker = 0;
+  } else if (true) {
+    var marker = 1;
+  } else if (f == arr[0]) {
+    var marker = 2;
+  } else if (f == arr[1]) {
+    var marker = 3;
+  } else if (true) {
+    var marker = 4;
+  } else {
+    var marker = 5;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const f = 1.0;
+  const arr = array<f32, 2u>(f, f);
+  if (true) {
+    var marker = 0;
+  } else if (true) {
+    var marker = 1;
+  } else {
+    let tint_symbol = arr;
+    if ((f == tint_symbol[0])) {
+      var marker = 2;
+    } else {
+      let tint_symbol_1 = arr;
+      if ((f == tint_symbol_1[1])) {
+        var marker = 3;
+      } else if (true) {
+        var marker = 4;
+      } else {
+        var marker = 5;
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInElseIfChain) {
+    auto* src = R"(
+const f = 1.0;
+
+const arr = array<f32, 2u>(f, f);
+
+fn F() {
+  if (true) {
+    var marker = 0;
+  } else if (true) {
+    var marker = 1;
+  } else if (f == arr[0]) {
+    var marker = 2;
+  } else if (f == arr[1]) {
+    var marker = 3;
+  } else if (true) {
+    var marker = 4;
+  } else {
+    var marker = 5;
+  }
+}
+)";
+
+    auto* expect = R"(
+const f = 1.0;
+
+const arr = array<f32, 2u>(f, f);
+
+fn F() {
+  if (true) {
+    var marker = 0;
+  } else if (true) {
+    var marker = 1;
+  } else {
+    let tint_symbol = arr;
+    if ((f == tint_symbol[0])) {
+      var marker = 2;
+    } else {
+      let tint_symbol_1 = arr;
+      if ((f == tint_symbol_1[1])) {
+        var marker = 3;
+      } else if (true) {
+        var marker = 4;
+      } else {
+        var marker = 5;
+      }
+    }
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ArrayInArrayArray) {
+    auto* src = R"(
+fn f() {
+  var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[0][1];
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  let tint_symbol = array<f32, 2u>(1.0, 2.0);
+  let tint_symbol_1 = array<f32, 2u>(3.0, 4.0);
+  let tint_symbol_2 = array<array<f32, 2u>, 2u>(tint_symbol, tint_symbol_1);
+  var i = tint_symbol_2[0][1];
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArray) {
+    auto* src = R"(
+fn f() {
+  const arr_0 = array<f32, 2u>(1.0, 2.0);
+  const arr_1 = array<f32, 2u>(3.0, 4.0);
+  const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
+  var i = arr_2[0][1];
+}
+)";
+
+    auto* expect = R"(
+fn f() {
+  const arr_0 = array<f32, 2u>(1.0, 2.0);
+  const arr_1 = array<f32, 2u>(3.0, 4.0);
+  const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
+  let tint_symbol = arr_2;
+  var i = tint_symbol[0][1];
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInArrayArray) {
+    auto* src = R"(
+const arr_0 = array<f32, 2u>(1.0, 2.0);
+
+const arr_1 = array<f32, 2u>(3.0, 4.0);
+
+const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
+
+fn f() {
+  var i = arr_2[0][1];
+}
+)";
+
+    auto* expect = R"(
+const arr_0 = array<f32, 2u>(1.0, 2.0);
+
+const arr_1 = array<f32, 2u>(3.0, 4.0);
+
+const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1);
+
+fn f() {
+  let tint_symbol = arr_2;
+  var i = tint_symbol[0][1];
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, StructNested) {
+    auto* src = R"(
+struct S1 {
+  a : i32,
+};
+
+struct S2 {
+  a : i32,
+  b : S1,
+  c : i32,
+};
+
+struct S3 {
+  a : S2,
+};
+
+fn f() {
+  var x = S3(S2(1, S1(2), 3)).a.b.a;
+}
+)";
+
+    auto* expect = R"(
+struct S1 {
+  a : i32,
+}
+
+struct S2 {
+  a : i32,
+  b : S1,
+  c : i32,
+}
+
+struct S3 {
+  a : S2,
+}
+
+fn f() {
+  let tint_symbol = S1(2);
+  let tint_symbol_1 = S2(1, tint_symbol, 3);
+  let tint_symbol_2 = S3(tint_symbol_1);
+  var x = tint_symbol_2.a.b.a;
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, Mixed) {
+    auto* src = R"(
+struct S1 {
+  a : i32,
+};
+
+struct S2 {
+  a : array<S1, 3u>,
+};
+
+fn f() {
+  var x = S2(array<S1, 3u>(S1(1), S1(2), S1(3))).a[1].a;
+}
+)";
+
+    auto* expect = R"(
+struct S1 {
+  a : i32,
+}
+
+struct S2 {
+  a : array<S1, 3u>,
+}
+
+fn f() {
+  let tint_symbol = S1(1);
+  let tint_symbol_1 = S1(2);
+  let tint_symbol_2 = S1(3);
+  let tint_symbol_3 = array<S1, 3u>(tint_symbol, tint_symbol_1, tint_symbol_2);
+  let tint_symbol_4 = S2(tint_symbol_3);
+  var x = tint_symbol_4.a[1].a;
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, Mixed_OutOfOrder) {
+    auto* src = R"(
+fn f() {
+  var x = S2(array<S1, 3u>(S1(1), S1(2), S1(3))).a[1].a;
+}
+
+struct S2 {
+  a : array<S1, 3u>,
+};
+
+struct S1 {
+  a : i32,
+};
+)";
+
+    auto* expect = R"(
+fn f() {
+  let tint_symbol = S1(1);
+  let tint_symbol_1 = S1(2);
+  let tint_symbol_2 = S1(3);
+  let tint_symbol_3 = array<S1, 3u>(tint_symbol, tint_symbol_1, tint_symbol_2);
+  let tint_symbol_4 = S2(tint_symbol_3);
+  var x = tint_symbol_4.a[1].a;
+}
+
+struct S2 {
+  a : array<S1, 3u>,
+}
+
+struct S1 {
+  a : i32,
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl) {
+    auto* src = R"(
+type F = f32;
+
+fn f() {
+  var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+  var local_str = F(3.0);
+}
+
+const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+
+const module_str : F = F(2.0);
+)";
+
+    auto* expect = src;
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl_OutOfOrder) {
+    auto* src = R"(
+fn f() {
+  var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+  var local_str = F(3.0);
+}
+
+const module_str : F = F(2.0);
+
+type F = f32;
+
+const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
+)";
+
+    auto* expect = src;
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(PromoteInitializersToLetTest, ForLoopShadowing) {
+    auto* src = R"(
+fn X() {
+  var i = 10;
+  for(var f = 0; f < 10; f = f + array<i32, 1u>(i)[0]) {
+      var i = 20;
+  }
+}
+
+fn Y() {
+  var i = 10;
+  for(var f = 0; f < array<i32, 1u>(i)[0]; f = f + 1) {
+      var i = 20;
+  }
+}
+
+fn Z() {
+  var i = 10;
+  for(var f = array<i32, 1u>(i)[0]; f < 10; f = f + 1) {
+      var i = 20;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn X() {
+  var i = 10;
+  {
+    var f = 0;
+    loop {
+      if (!((f < 10))) {
+        break;
+      }
+      {
+        var i = 20;
+      }
+
+      continuing {
+        let tint_symbol = array<i32, 1u>(i);
+        f = (f + tint_symbol[0]);
+      }
+    }
+  }
+}
+
+fn Y() {
+  var i = 10;
+  {
+    var f = 0;
+    loop {
+      let tint_symbol_1 = array<i32, 1u>(i);
+      if (!((f < tint_symbol_1[0]))) {
+        break;
+      }
+      {
+        var i = 20;
+      }
+
+      continuing {
+        f = (f + 1);
+      }
+    }
+  }
+}
+
+fn Z() {
+  var i = 10;
+  let tint_symbol_2 = array<i32, 1u>(i);
+  for(var f = tint_symbol_2[0]; (f < 10); f = (f + 1)) {
+    var i = 20;
+  }
+}
+)";
+
+    DataMap data;
+    auto got = Run<PromoteInitializersToLet>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/promote_side_effects_to_decl.cc b/src/tint/transform/promote_side_effects_to_decl.cc
index d527a4c..7551c23 100644
--- a/src/tint/transform/promote_side_effects_to_decl.cc
+++ b/src/tint/transform/promote_side_effects_to_decl.cc
@@ -275,7 +275,7 @@
                 if (auto* sem_e = sem.Get(e)) {
                     if (auto* var_user = sem_e->As<sem::VariableUser>()) {
                         // Don't hoist constants.
-                        if (var_user->ConstantValue().IsValid()) {
+                        if (var_user->ConstantValue()) {
                             return false;
                         }
                         // Don't hoist read-only variables as they cannot receive
diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc
index aab1e0c..beb1108 100644
--- a/src/tint/transform/robustness.cc
+++ b/src/tint/transform/robustness.cc
@@ -120,17 +120,18 @@
             return nullptr;
         }
 
-        if (auto idx_constant = idx_sem->ConstantValue()) {
+        if (auto* idx_constant = idx_sem->ConstantValue()) {
             // Constant value index
-            if (idx_constant.Type()->Is<sem::I32>()) {
-                idx.i32 = static_cast<int32_t>(idx_constant.Element<AInt>(0).value);
+            auto val = std::get<AInt>(idx_constant->Value());
+            if (idx_constant->Type()->Is<sem::I32>()) {
+                idx.i32 = static_cast<int32_t>(val);
                 idx.is_signed = true;
-            } else if (idx_constant.Type()->Is<sem::U32>()) {
-                idx.u32 = static_cast<uint32_t>(idx_constant.Element<AInt>(0).value);
+            } else if (idx_constant->Type()->Is<sem::U32>()) {
+                idx.u32 = static_cast<uint32_t>(val);
                 idx.is_signed = false;
             } else {
                 TINT_ICE(Transform, b.Diagnostics()) << "unsupported constant value for accessor "
-                                                     << idx_constant.Type()->TypeInfo().name;
+                                                     << idx_constant->Type()->TypeInfo().name;
                 return nullptr;
             }
         } else {
@@ -223,9 +224,9 @@
         auto array_idx = signature.IndexOf(sem::ParameterUsage::kArrayIndex);
         auto level_idx = signature.IndexOf(sem::ParameterUsage::kLevel);
 
-        auto* texture_arg = expr->args[texture_idx];
-        auto* coords_arg = expr->args[coords_idx];
-        auto* coords_ty = builtin->Parameters()[coords_idx]->Type();
+        auto* texture_arg = expr->args[static_cast<size_t>(texture_idx)];
+        auto* coords_arg = expr->args[static_cast<size_t>(coords_idx)];
+        auto* coords_ty = builtin->Parameters()[static_cast<size_t>(coords_idx)]->Type();
 
         // If the level is provided, then we need to clamp this. As the level is
         // used by textureDimensions() and the texture[Load|Store]() calls, we need
@@ -235,7 +236,7 @@
         std::function<const ast::Expression*()> level_arg;
         if (level_idx >= 0) {
             level_arg = [&] {
-                auto* arg = expr->args[level_idx];
+                auto* arg = expr->args[static_cast<size_t>(level_idx)];
                 auto* num_levels = b.Call("textureNumLevels", ctx.Clone(texture_arg));
                 auto* zero = b.Expr(0_i);
                 auto* max = ctx.dst->Sub(num_levels, 1_i);
@@ -258,7 +259,7 @@
 
         // Clamp the array_index argument, if provided
         if (array_idx >= 0) {
-            auto* arg = expr->args[array_idx];
+            auto* arg = expr->args[static_cast<size_t>(array_idx)];
             auto* num_layers = b.Call("textureNumLayers", ctx.Clone(texture_arg));
             auto* zero = b.Expr(0_i);
             auto* max = ctx.dst->Sub(num_layers, 1_i);
@@ -268,7 +269,7 @@
 
         // Clamp the level argument, if provided
         if (level_idx >= 0) {
-            auto* arg = expr->args[level_idx];
+            auto* arg = expr->args[static_cast<size_t>(level_idx)];
             ctx.Replace(arg, level_arg ? level_arg() : ctx.dst->Expr(0_i));
         }
 
diff --git a/src/tint/transform/robustness_test.cc b/src/tint/transform/robustness_test.cc
index fb0e5a4..8dab595 100644
--- a/src/tint/transform/robustness_test.cc
+++ b/src/tint/transform/robustness_test.cc
@@ -21,7 +21,7 @@
 
 using RobustnessTest = TransformTest;
 
-TEST_F(RobustnessTest, Array_Idx_Clamp) {
+TEST_F(RobustnessTest, Array_Let_Idx_Clamp) {
     auto* src = R"(
 var<private> a : array<f32, 3>;
 
@@ -35,7 +35,7 @@
     auto* expect = R"(
 var<private> a : array<f32, 3>;
 
-let c : u32 = 1u;
+const c : u32 = 1u;
 
 fn f() {
   let b : f32 = a[1u];
@@ -47,7 +47,33 @@
     EXPECT_EQ(expect, str(got));
 }
 
-TEST_F(RobustnessTest, Array_Idx_Clamp_OutOfOrder) {
+TEST_F(RobustnessTest, Array_Const_Idx_Clamp) {
+    auto* src = R"(
+var<private> a : array<f32, 3>;
+
+const c : u32 = 1u;
+
+fn f() {
+  let b : f32 = a[c];
+}
+)";
+
+    auto* expect = R"(
+var<private> a : array<f32, 3>;
+
+const c : u32 = 1u;
+
+fn f() {
+  let b : f32 = a[1u];
+}
+)";
+
+    auto got = Run<Robustness>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(RobustnessTest, Array_Let_Idx_Clamp_OutOfOrder) {
     auto* src = R"(
 fn f() {
   let b : f32 = a[c];
@@ -63,7 +89,33 @@
   let b : f32 = a[1u];
 }
 
-let c : u32 = 1u;
+const c : u32 = 1u;
+
+var<private> a : array<f32, 3>;
+)";
+
+    auto got = Run<Robustness>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(RobustnessTest, Array_Const_Idx_Clamp_OutOfOrder) {
+    auto* src = R"(
+fn f() {
+  let b : f32 = a[c];
+}
+
+const c : u32 = 1u;
+
+var<private> a : array<f32, 3>;
+)";
+
+    auto* expect = R"(
+fn f() {
+  let b : f32 = a[1u];
+}
+
+const c : u32 = 1u;
 
 var<private> a : array<f32, 3>;
 )";
@@ -1392,7 +1444,7 @@
 
 @group(0) @binding(0) var<storage, read> s : S;
 
-let c : u32 = 1u;
+const c : u32 = 1u;
 
 fn f() {
   let b : f32 = s.b[c];
@@ -1409,7 +1461,7 @@
 
 @group(0) @binding(0) var<storage, read> s : S;
 
-let c : u32 = 1u;
+const c : u32 = 1u;
 
 fn f() {
   let b : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
diff --git a/src/tint/transform/single_entry_point_test.cc b/src/tint/transform/single_entry_point_test.cc
index 8445f61..020bd65 100644
--- a/src/tint/transform/single_entry_point_test.cc
+++ b/src/tint/transform/single_entry_point_test.cc
@@ -186,13 +186,13 @@
 
 TEST_F(SingleEntryPointTest, GlobalConstants) {
     auto* src = R"(
-let a : f32 = 1.0;
+const a : f32 = 1.0;
 
-let b : f32 = 1.0;
+const b : f32 = 1.0;
 
-let c : f32 = 1.0;
+const c : f32 = 1.0;
 
-let d : f32 = 1.0;
+const d : f32 = 1.0;
 
 @vertex
 fn vert_main() -> @builtin(position) vec4<f32> {
@@ -217,7 +217,7 @@
 )";
 
     auto* expect = R"(
-let c : f32 = 1.0;
+const c : f32 = 1.0;
 
 @compute @workgroup_size(1)
 fn comp_main1() {
@@ -243,6 +243,32 @@
 }
 )";
 
+    auto* expect = R"(
+const size : i32 = 1;
+
+@compute @workgroup_size(size)
+fn main() {
+}
+)";
+
+    SingleEntryPoint::Config cfg("main");
+
+    DataMap data;
+    data.Add<SingleEntryPoint::Config>(cfg);
+    auto got = Run<SingleEntryPoint>(src, data);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SingleEntryPointTest, WorkgroupSizeConstPreserved) {
+    auto* src = R"(
+const size : i32 = 1;
+
+@compute @workgroup_size(size)
+fn main() {
+}
+)";
+
     auto* expect = src;
 
     SingleEntryPoint::Config cfg("main");
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
new file mode 100644
index 0000000..dc5b35c
--- /dev/null
+++ b/src/tint/transform/spirv_atomic.cc
@@ -0,0 +1,299 @@
+// Copyright 2022 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/transform/spirv_atomic.h"
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/block_statement.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/index_accessor_expression.h"
+#include "src/tint/sem/member_accessor_expression.h"
+#include "src/tint/sem/reference.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/utils/map.h"
+#include "src/tint/utils/unique_vector.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::SpirvAtomic);
+TINT_INSTANTIATE_TYPEINFO(tint::transform::SpirvAtomic::Stub);
+
+namespace tint::transform {
+
+/// Private implementation of transform
+struct SpirvAtomic::State {
+  private:
+    /// A struct that has been forked because a subset of members were made atomic.
+    struct ForkedStruct {
+        Symbol name;
+        std::unordered_set<size_t> atomic_members;
+    };
+
+    CloneContext& ctx;
+    ProgramBuilder& b = *ctx.dst;
+    std::unordered_map<const ast::Struct*, ForkedStruct> forked_structs;
+    std::unordered_set<const sem::Variable*> atomic_variables;
+    utils::UniqueVector<const sem::Expression*> atomic_expressions;
+
+  public:
+    /// Constructor
+    /// @param c the clone context
+    explicit State(CloneContext& c) : ctx(c) {}
+
+    /// Runs the transform
+    void Run() {
+        // Look for stub functions generated by the SPIR-V reader, which are used as placeholders
+        // for atomic builtin calls.
+        for (auto* fn : ctx.src->AST().Functions()) {
+            if (auto* stub = ast::GetAttribute<Stub>(fn->attributes)) {
+                auto* sem = ctx.src->Sem().Get(fn);
+
+                for (auto* call : sem->CallSites()) {
+                    // The first argument is always the atomic.
+                    // The stub passes this by value, whereas the builtin wants a pointer.
+                    // Take the address of the atomic argument.
+                    auto& args = call->Declaration()->args;
+                    auto out_args = ctx.Clone(args);
+                    out_args[0] = b.AddressOf(out_args[0]);
+
+                    // Replace all callsites of this stub to a call to the real builtin
+                    if (stub->builtin == sem::BuiltinType::kAtomicCompareExchangeWeak) {
+                        // atomicCompareExchangeWeak returns a struct, so insert a call to it above
+                        // the current statement, and replace the current call with the struct's
+                        // `old_value` member.
+                        auto* block = call->Stmt()->Block()->Declaration();
+                        auto old_value = b.Symbols().New("old_value");
+                        auto old_value_decl = b.Decl(b.Let(
+                            old_value, nullptr,
+                            b.MemberAccessor(b.Call(sem::str(stub->builtin), std::move(out_args)),
+                                             b.Expr("old_value"))));
+                        ctx.InsertBefore(block->statements, call->Stmt()->Declaration(),
+                                         old_value_decl);
+                        ctx.Replace(call->Declaration(), b.Expr(old_value));
+                    } else {
+                        ctx.Replace(call->Declaration(),
+                                    b.Call(sem::str(stub->builtin), std::move(out_args)));
+                    }
+
+                    // Keep track of this expression. We'll need to modify the source variable /
+                    // structure to be atomic.
+                    atomic_expressions.add(ctx.src->Sem().Get(args[0]));
+                }
+
+                // Remove the stub from the output program
+                ctx.Remove(ctx.src->AST().GlobalDeclarations(), fn);
+            }
+        }
+
+        // Transform all variables and structure members that were used in atomic operations as
+        // atomic types. This propagates up originating expression chains.
+        ProcessAtomicExpressions();
+
+        // If we need to change structure members, then fork them.
+        if (!forked_structs.empty()) {
+            ctx.ReplaceAll([&](const ast::Struct* str) {
+                // Always emit the original structure. This allows unrelated usage of the structure
+                // to continue working.
+                // auto* original = ctx.CloneWithoutTransform(str);
+
+                // Is `str` a structure we need to fork?
+                if (auto it = forked_structs.find(str); it != forked_structs.end()) {
+                    const auto& forked = it->second;
+
+                    // Re-create the structure swapping in the atomic-flavoured members
+                    std::vector<const ast::StructMember*> members(str->members.size());
+                    for (size_t i = 0; i < str->members.size(); i++) {
+                        auto* member = str->members[i];
+                        if (forked.atomic_members.count(i)) {
+                            auto* type = AtomicTypeFor(ctx.src->Sem().Get(member)->Type());
+                            auto name = ctx.src->Symbols().NameFor(member->symbol);
+                            members[i] = b.Member(name, type, ctx.Clone(member->attributes));
+                        } else {
+                            members[i] = ctx.Clone(member);
+                        }
+                    }
+                    b.Structure(forked.name, std::move(members));
+                }
+
+                // return original;
+                return nullptr;
+            });
+        }
+
+        // Replace assignments and decls from atomic variables with atomicLoads, and assignments to
+        // atomic variables with atomicStores.
+        ReplaceLoadsAndStores();
+
+        ctx.Clone();
+    }
+
+  private:
+    ForkedStruct& Fork(const ast::Struct* str) {
+        auto& forked = forked_structs[str];
+        if (!forked.name.IsValid()) {
+            forked.name = b.Symbols().New(ctx.src->Symbols().NameFor(str->name) + "_atomic");
+        }
+        return forked;
+    }
+
+    void ProcessAtomicExpressions() {
+        for (size_t i = 0; i < atomic_expressions.size(); i++) {
+            Switch(
+                atomic_expressions[i],  //
+                [&](const sem::VariableUser* user) {
+                    auto* v = user->Variable()->Declaration();
+                    if (v->type && atomic_variables.emplace(user->Variable()).second) {
+                        ctx.Replace(v->type, AtomicTypeFor(user->Variable()->Type()));
+                    }
+                    if (auto* ctor = user->Variable()->Constructor()) {
+                        atomic_expressions.add(ctor);
+                    }
+                },
+                [&](const sem::StructMemberAccess* access) {
+                    // Fork the struct (the first time) and mark member(s) that need to be made
+                    // atomic.
+                    auto* member = access->Member();
+                    Fork(member->Struct()->Declaration()).atomic_members.emplace(member->Index());
+                    atomic_expressions.add(access->Object());
+                },
+                [&](const sem::IndexAccessorExpression* index) {
+                    atomic_expressions.add(index->Object());
+                },
+                [&](const sem::Expression* e) {
+                    if (auto* unary = e->Declaration()->As<ast::UnaryOpExpression>()) {
+                        atomic_expressions.add(ctx.src->Sem().Get(unary->expr));
+                    }
+                });
+        }
+    }
+
+    const ast::Type* AtomicTypeFor(const sem::Type* ty) {
+        return Switch(
+            ty,  //
+            [&](const sem::I32*) { return b.ty.atomic(CreateASTTypeFor(ctx, ty)); },
+            [&](const sem::U32*) { return b.ty.atomic(CreateASTTypeFor(ctx, ty)); },
+            [&](const sem::Struct* str) { return b.ty.type_name(Fork(str->Declaration()).name); },
+            [&](const sem::Array* arr) {
+                return arr->IsRuntimeSized()
+                           ? b.ty.array(AtomicTypeFor(arr->ElemType()))
+                           : b.ty.array(AtomicTypeFor(arr->ElemType()), u32(arr->Count()));
+            },
+            [&](const sem::Pointer* ptr) {
+                return b.ty.pointer(AtomicTypeFor(ptr->StoreType()), ptr->StorageClass(),
+                                    ptr->Access());
+            },
+            [&](const sem::Reference* ref) { return AtomicTypeFor(ref->StoreType()); },
+            [&](Default) {
+                TINT_ICE(Transform, b.Diagnostics())
+                    << "unhandled type: " << ty->FriendlyName(ctx.src->Symbols());
+                return nullptr;
+            });
+    }
+
+    void ReplaceLoadsAndStores() {
+        // Returns true if 'e' is a reference to an atomic variable or struct member
+        auto is_ref_to_atomic_var = [&](const sem::Expression* e) {
+            if (tint::Is<sem::Reference>(e->Type()) && e->SourceVariable() &&
+                (atomic_variables.count(e->SourceVariable()) != 0)) {
+                // If it's a struct member, make sure it's one we marked as atomic
+                if (auto* ma = e->As<sem::StructMemberAccess>()) {
+                    auto it = forked_structs.find(ma->Member()->Struct()->Declaration());
+                    if (it != forked_structs.end()) {
+                        auto& forked = it->second;
+                        return forked.atomic_members.count(ma->Member()->Index()) != 0;
+                    }
+                }
+                return true;
+            }
+            return false;
+        };
+
+        // Look for loads and stores via assignments and decls of atomic variables we've collected
+        // so far, and replace them with atomicLoad and atomicStore.
+        for (auto* atomic_var : atomic_variables) {
+            for (auto* vu : atomic_var->Users()) {
+                Switch(
+                    vu->Stmt()->Declaration(),
+                    [&](const ast::AssignmentStatement* assign) {
+                        auto* sem_lhs = ctx.src->Sem().Get(assign->lhs);
+                        if (is_ref_to_atomic_var(sem_lhs)) {
+                            ctx.Replace(assign, [=] {
+                                auto* lhs = ctx.CloneWithoutTransform(assign->lhs);
+                                auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
+                                auto* call = b.Call(sem::str(sem::BuiltinType::kAtomicStore),
+                                                    b.AddressOf(lhs), rhs);
+                                return b.CallStmt(call);
+                            });
+                            return;
+                        }
+
+                        auto sem_rhs = ctx.src->Sem().Get(assign->rhs);
+                        if (is_ref_to_atomic_var(sem_rhs)) {
+                            ctx.Replace(assign->rhs, [=] {
+                                auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
+                                return b.Call(sem::str(sem::BuiltinType::kAtomicLoad),
+                                              b.AddressOf(rhs));
+                            });
+                            return;
+                        }
+                    },
+                    [&](const ast::VariableDeclStatement* decl) {
+                        auto* var = decl->variable;
+                        if (auto* sem_ctor = ctx.src->Sem().Get(var->constructor)) {
+                            if (is_ref_to_atomic_var(sem_ctor)) {
+                                ctx.Replace(var->constructor, [=] {
+                                    auto* rhs = ctx.CloneWithoutTransform(var->constructor);
+                                    return b.Call(sem::str(sem::BuiltinType::kAtomicLoad),
+                                                  b.AddressOf(rhs));
+                                });
+                                return;
+                            }
+                        }
+                    });
+            }
+        }
+    }
+};
+
+SpirvAtomic::SpirvAtomic() = default;
+SpirvAtomic::~SpirvAtomic() = default;
+
+SpirvAtomic::Stub::Stub(ProgramID pid, sem::BuiltinType b) : Base(pid), builtin(b) {}
+SpirvAtomic::Stub::~Stub() = default;
+std::string SpirvAtomic::Stub::InternalName() const {
+    return "@internal(spirv-atomic " + std::string(sem::str(builtin)) + ")";
+}
+
+const SpirvAtomic::Stub* SpirvAtomic::Stub::Clone(CloneContext* ctx) const {
+    return ctx->dst->ASTNodes().Create<SpirvAtomic::Stub>(ctx->dst->ID(), builtin);
+}
+
+bool SpirvAtomic::ShouldRun(const Program* program, const DataMap&) const {
+    for (auto* fn : program->AST().Functions()) {
+        if (ast::HasAttribute<Stub>(fn->attributes)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void SpirvAtomic::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+    State{ctx}.Run();
+}
+
+}  // namespace tint::transform
diff --git a/src/tint/transform/spirv_atomic.h b/src/tint/transform/spirv_atomic.h
new file mode 100644
index 0000000..36ac842
--- /dev/null
+++ b/src/tint/transform/spirv_atomic.h
@@ -0,0 +1,84 @@
+// Copyright 2022 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_TRANSFORM_SPIRV_ATOMIC_H_
+#define SRC_TINT_TRANSFORM_SPIRV_ATOMIC_H_
+
+#include <string>
+
+#include "src/tint/ast/internal_attribute.h"
+#include "src/tint/sem/builtin_type.h"
+#include "src/tint/transform/transform.h"
+
+// Forward declarations
+namespace tint {
+class CloneContext;
+}  // namespace tint
+
+namespace tint::transform {
+
+/// SpirvAtomic is a transform that replaces calls to stub functions created by the SPIR-V reader
+/// with calls to the WGSL atomic builtin. It also makes sure to replace variable declarations that
+/// are the target of the atomic operations with an atomic declaration of the same type. For
+/// structs, it creates a copy of the original struct with atomic members.
+class SpirvAtomic final : public Castable<SpirvAtomic, Transform> {
+  public:
+    /// Constructor
+    SpirvAtomic();
+    /// Destructor
+    ~SpirvAtomic() override;
+
+    /// Stub is an attribute applied to stub SPIR-V reader generated functions that need to be
+    /// translated to an atomic builtin.
+    class Stub final : public Castable<Stub, ast::InternalAttribute> {
+      public:
+        /// @param program_id the identifier of the program that owns this node
+        /// @param builtin the atomic builtin this stub represents
+        Stub(ProgramID program_id, sem::BuiltinType builtin);
+        /// Destructor
+        ~Stub() override;
+
+        /// @return a short description of the internal attribute which will be
+        /// displayed as `@internal(<name>)`
+        std::string InternalName() const override;
+
+        /// Performs a deep clone of this object using the CloneContext `ctx`.
+        /// @param ctx the clone context
+        /// @return the newly cloned object
+        const Stub* Clone(CloneContext* ctx) const override;
+
+        /// The type of the intrinsic
+        const sem::BuiltinType builtin;
+    };
+
+    /// @param program the program to inspect
+    /// @param data optional extra transform-specific input data
+    /// @returns true if this transform should be run for the given program
+    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+
+  protected:
+    struct State;
+
+    /// Runs the transform using the CloneContext built for transforming a
+    /// program. Run() is responsible for calling Clone() on the CloneContext.
+    /// @param ctx the CloneContext primed with the input program and
+    /// ProgramBuilder
+    /// @param inputs optional extra transform-specific input data
+    /// @param outputs optional extra transform-specific output data
+    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+};
+
+}  // namespace tint::transform
+
+#endif  // SRC_TINT_TRANSFORM_SPIRV_ATOMIC_H_
diff --git a/src/tint/transform/spirv_atomic_test.cc b/src/tint/transform/spirv_atomic_test.cc
new file mode 100644
index 0000000..7f07d06
--- /dev/null
+++ b/src/tint/transform/spirv_atomic_test.cc
@@ -0,0 +1,1383 @@
+// Copyright 2022 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/transform/spirv_atomic.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/tint/reader/wgsl/parser_impl.h"
+#include "src/tint/transform/test_helper.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::transform {
+namespace {
+
+class SpirvAtomicTest : public TransformTest {
+  public:
+    Output Run(std::string in) {
+        auto file = std::make_unique<Source::File>("test", std::move(in));
+        auto parser = reader::wgsl::ParserImpl(file.get());
+        parser.Parse();
+
+        auto& b = parser.builder();
+
+        sem::BuiltinType two_params[] = {
+            sem::BuiltinType::kAtomicExchange, sem::BuiltinType::kAtomicAdd,
+            sem::BuiltinType::kAtomicSub,      sem::BuiltinType::kAtomicMin,
+            sem::BuiltinType::kAtomicMax,      sem::BuiltinType::kAtomicAnd,
+            sem::BuiltinType::kAtomicOr,       sem::BuiltinType::kAtomicXor,
+        };
+        for (auto& a : two_params) {
+            b.Func(std::string{"stub_"} + sem::str(a) + "_u32",
+                   {
+                       b.Param("p0", b.ty.u32()),
+                       b.Param("p1", b.ty.u32()),
+                   },
+                   b.ty.u32(), {b.Return(0_u)},
+                   {b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), a)});
+            b.Func(std::string{"stub_"} + sem::str(a) + "_i32",
+                   {
+                       b.Param("p0", b.ty.i32()),
+                       b.Param("p1", b.ty.i32()),
+                   },
+                   b.ty.i32(), {b.Return(0_i)},
+                   {b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), a)});
+        }
+
+        b.Func("stub_atomicLoad_u32",
+               {
+                   b.Param("p0", b.ty.u32()),
+               },
+               b.ty.u32(), {b.Return(0_u)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicLoad),
+               });
+        b.Func("stub_atomicLoad_i32",
+               {
+                   b.Param("p0", b.ty.i32()),
+               },
+               b.ty.i32(), {b.Return(0_i)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicLoad),
+               });
+
+        b.Func("stub_atomicStore_u32",
+               {
+                   b.Param("p0", b.ty.u32()),
+                   b.Param("p1", b.ty.u32()),
+               },
+               b.ty.void_(), {},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicStore),
+               });
+        b.Func("stub_atomicStore_i32",
+               {
+                   b.Param("p0", b.ty.i32()),
+                   b.Param("p1", b.ty.i32()),
+               },
+               b.ty.void_(), {},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicStore),
+               });
+
+        b.Func("stub_atomic_compare_exchange_weak_u32",
+               {
+                   b.Param("p0", b.ty.u32()),
+                   b.Param("p1", b.ty.u32()),
+                   b.Param("p2", b.ty.u32()),
+               },
+               b.ty.u32(), {b.Return(0_u)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(
+                       b.ID(), sem::BuiltinType::kAtomicCompareExchangeWeak),
+               });
+        b.Func("stub_atomic_compare_exchange_weak_i32",
+               {b.Param("p0", b.ty.i32()), b.Param("p1", b.ty.i32()), b.Param("p2", b.ty.i32())},
+               b.ty.i32(), {b.Return(0_i)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(
+                       b.ID(), sem::BuiltinType::kAtomicCompareExchangeWeak),
+               });
+
+        // Keep this pointer alive after Transform() returns
+        files_.emplace_back(std::move(file));
+
+        return TransformTest::Run<SpirvAtomic>(Program(std::move(b)));
+    }
+
+  private:
+    std::vector<std::unique_ptr<Source::File>> files_;
+};
+
+TEST_F(SpirvAtomicTest, ArrayOfU32) {
+    auto* src = R"(
+var<workgroup> wg : array<u32, 4>;
+
+fn f() {
+  stub_atomicStore_u32(wg[1], 1u);
+}
+)";
+
+    auto* expect = R"(
+var<workgroup> wg : array<atomic<u32>, 4u>;
+
+fn f() {
+  atomicStore(&(wg[1]), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ArraysOfU32) {
+    auto* src = R"(
+var<workgroup> wg : array<array<array<u32, 1>, 2>, 3>;
+
+fn f() {
+  stub_atomicStore_u32(wg[2][1][0], 1u);
+}
+)";
+
+    auto* expect = R"(
+var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
+
+fn f() {
+  atomicStore(&(wg[2][1][0]), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AliasedArraysOfU32) {
+    auto* src = R"(
+type A0 = u32;
+
+type A1 = array<A0, 1>;
+
+type A2 = array<A1, 2>;
+
+type A3 = array<A2, 3>;
+
+var<workgroup> wg : A3;
+
+fn f() {
+  stub_atomicStore_u32(wg[2][1][0], 1u);
+}
+)";
+
+    auto* expect = R"(
+type A0 = u32;
+
+type A1 = array<A0, 1>;
+
+type A2 = array<A1, 2>;
+
+type A3 = array<A2, 3>;
+
+var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
+
+fn f() {
+  atomicStore(&(wg[2][1][0]), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, FlatStructSingleAtomic) {
+    auto* src = R"(
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : S;
+
+fn f() {
+  stub_atomicStore_u32(wg.a, 1u);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+}
+
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f() {
+  atomicStore(&(wg.a), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, FlatStructMultipleAtomic) {
+    auto* src = R"(
+struct S {
+  a : u32,
+  b : i32,
+}
+
+var<workgroup> wg : S;
+
+fn f1() {
+  stub_atomicStore_u32(wg.a, 1u);
+}
+
+fn f2() {
+  stub_atomicStore_i32(wg.b, 2i);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+  b : atomic<i32>,
+}
+
+struct S {
+  a : u32,
+  b : i32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f1() {
+  atomicStore(&(wg.a), 1u);
+}
+
+fn f2() {
+  atomicStore(&(wg.b), 2i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, NestedStruct) {
+    auto* src = R"(
+struct S0 {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+struct S1 {
+  a : i32,
+  b : u32,
+  c : S0,
+}
+
+struct S2 {
+  a : i32,
+  b : S1,
+  c : u32,
+}
+
+var<workgroup> wg : S2;
+
+fn f() {
+  stub_atomicStore_u32(wg.b.c.a, 1u);
+}
+)";
+
+    auto* expect = R"(
+struct S0_atomic {
+  a : atomic<u32>,
+  b : i32,
+  c : u32,
+}
+
+struct S0 {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+struct S1_atomic {
+  a : i32,
+  b : u32,
+  c : S0_atomic,
+}
+
+struct S1 {
+  a : i32,
+  b : u32,
+  c : S0,
+}
+
+struct S2_atomic {
+  a : i32,
+  b : S1_atomic,
+  c : u32,
+}
+
+struct S2 {
+  a : i32,
+  b : S1,
+  c : u32,
+}
+
+var<workgroup> wg : S2_atomic;
+
+fn f() {
+  atomicStore(&(wg.b.c.a), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ArrayOfStruct) {
+    auto* src = R"(
+struct S {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+@group(0) @binding(1) var<storage, read_write> arr : array<S>;
+
+fn f() {
+  stub_atomicStore_i32(arr[4].b, 1i);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : u32,
+  b : atomic<i32>,
+  c : u32,
+}
+
+struct S {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+@group(0) @binding(1) var<storage, read_write> arr : array<S_atomic>;
+
+fn f() {
+  atomicStore(&(arr[4].b), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, StructOfArray) {
+    auto* src = R"(
+struct S {
+  a : array<i32>,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  stub_atomicStore_i32(s.a[4], 1i);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : array<atomic<i32>>,
+}
+
+struct S {
+  a : array<i32>,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  atomicStore(&(s.a[4]), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ViaPtrLet) {
+    auto* src = R"(
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  let p0 = &(s);
+  let p1 : ptr<storage, i32, read_write> = &((*(p0)).i);
+  stub_atomicStore_i32(*p1, 1i);
+}
+)";
+
+    auto* expect =
+        R"(
+struct S_atomic {
+  i : atomic<i32>,
+}
+
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  let p0 = &(s);
+  let p1 : ptr<storage, atomic<i32>, read_write> = &((*(p0)).i);
+  atomicStore(&(*(p1)), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, StructIsolatedMixedUsage) {
+    auto* src = R"(
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  stub_atomicStore_i32(s.i, 1i);
+}
+
+fn another_usage() {
+  var s : S;
+  let x : i32 = s.i;
+  s.i = 3i;
+}
+)";
+
+    auto* expect =
+        R"(
+struct S_atomic {
+  i : atomic<i32>,
+}
+
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  atomicStore(&(s.i), 1i);
+}
+
+fn another_usage() {
+  var s : S;
+  let x : i32 = s.i;
+  s.i = 3i;
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// This sort of mixed usage isn't handled yet. Not sure if we need to just yet.
+// If we don't, then the transform should give sensible diagnostics instead of producing invalid
+// WGSL.
+// TODO(crbug.com/tint/1595)
+TEST_F(SpirvAtomicTest, DISABLED_StructComplexMixedUsage) {
+    auto* src = R"(
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  let x : i32 = s.i;
+  stub_atomicStore_i32(s.i, 1i);
+  s.i = 3i;
+}
+)";
+
+    auto* expect =
+        R"(
+struct S_atomic {
+  i : atomic<i32>,
+}
+
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  let x : i32 = atomicLoad(&s.i);
+  stub_atomicStore_i32(s.i, 1i);
+  atomicStore(&(s.i), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicLoad) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicLoad_u32(wg_u32);}
+  {let r = stub_atomicLoad_i32(wg_i32);}
+  {let r = stub_atomicLoad_u32(sg_u32);}
+  {let r = stub_atomicLoad_i32(sg_i32);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicLoad(&(wg_u32));
+  }
+  {
+    let r = atomicLoad(&(wg_i32));
+  }
+  {
+    let r = atomicLoad(&(sg_u32));
+  }
+  {
+    let r = atomicLoad(&(sg_i32));
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicExchange) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicExchange_u32(wg_u32, 123u);}
+  {let r = stub_atomicExchange_i32(wg_i32, 123i);}
+  {let r = stub_atomicExchange_u32(sg_u32, 123u);}
+  {let r = stub_atomicExchange_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicExchange(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicExchange(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicExchange(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicExchange(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicAdd) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicAdd_u32(wg_u32, 123u);}
+  {let r = stub_atomicAdd_i32(wg_i32, 123i);}
+  {let r = stub_atomicAdd_u32(sg_u32, 123u);}
+  {let r = stub_atomicAdd_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicAdd(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicAdd(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicAdd(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicAdd(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicSub) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicSub_u32(wg_u32, 123u);}
+  {let r = stub_atomicSub_i32(wg_i32, 123i);}
+  {let r = stub_atomicSub_u32(sg_u32, 123u);}
+  {let r = stub_atomicSub_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicSub(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicSub(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicSub(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicSub(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicMin) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicMin_u32(wg_u32, 123u);}
+  {let r = stub_atomicMin_i32(wg_i32, 123i);}
+  {let r = stub_atomicMin_u32(sg_u32, 123u);}
+  {let r = stub_atomicMin_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicMin(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicMin(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicMin(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicMin(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicMax) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicMax_u32(wg_u32, 123u);}
+  {let r = stub_atomicMax_i32(wg_i32, 123i);}
+  {let r = stub_atomicMax_u32(sg_u32, 123u);}
+  {let r = stub_atomicMax_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicMax(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicMax(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicMax(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicMax(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicAnd) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicAnd_u32(wg_u32, 123u);}
+  {let r = stub_atomicAnd_i32(wg_i32, 123i);}
+  {let r = stub_atomicAnd_u32(sg_u32, 123u);}
+  {let r = stub_atomicAnd_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicAnd(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicAnd(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicAnd(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicAnd(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicOr) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicOr_u32(wg_u32, 123u);}
+  {let r = stub_atomicOr_i32(wg_i32, 123i);}
+  {let r = stub_atomicOr_u32(sg_u32, 123u);}
+  {let r = stub_atomicOr_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicOr(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicOr(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicOr(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicOr(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicXor) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicXor_u32(wg_u32, 123u);}
+  {let r = stub_atomicXor_i32(wg_i32, 123i);}
+  {let r = stub_atomicXor_u32(sg_u32, 123u);}
+  {let r = stub_atomicXor_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicXor(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicXor(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicXor(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicXor(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicCompareExchangeWeak) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomic_compare_exchange_weak_u32(wg_u32, 123u, 456u);}
+  {let r = stub_atomic_compare_exchange_weak_i32(wg_i32, 123i, 456i);}
+  {let r = stub_atomic_compare_exchange_weak_u32(sg_u32, 123u, 456u);}
+  {let r = stub_atomic_compare_exchange_weak_i32(sg_i32, 123i, 456i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let old_value = atomicCompareExchangeWeak(&(wg_u32), 123u, 456u).old_value;
+    let r = old_value;
+  }
+  {
+    let old_value_2 = atomicCompareExchangeWeak(&(wg_i32), 123i, 456i).old_value;
+    let r = old_value_2;
+  }
+  {
+    let old_value_1 = atomicCompareExchangeWeak(&(sg_u32), 123u, 456u).old_value;
+    let r = old_value_1;
+  }
+  {
+    let old_value_3 = atomicCompareExchangeWeak(&(sg_i32), 123i, 456i).old_value;
+    let r = old_value_3;
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_Scaler) {
+    auto* src = R"(
+var<workgroup> wg : u32;
+
+fn f() {
+  stub_atomicAdd_u32(wg, 1u);
+
+  wg = 0u;
+  let a = wg;
+  var b : u32;
+  b = wg;
+}
+)";
+
+    auto* expect = R"(
+var<workgroup> wg : atomic<u32>;
+
+fn f() {
+  atomicAdd(&(wg), 1u);
+  atomicStore(&(wg), 0u);
+  let a = atomicLoad(&(wg));
+  var b : u32;
+  b = atomicLoad(&(wg));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_Struct) {
+    auto* src = R"(
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : S;
+
+fn f() {
+  stub_atomicAdd_u32(wg.a, 1u);
+
+  wg.a = 0u;
+  let a = wg.a;
+  var b : u32;
+  b = wg.a;
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+}
+
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f() {
+  atomicAdd(&(wg.a), 1u);
+  atomicStore(&(wg.a), 0u);
+  let a = atomicLoad(&(wg.a));
+  var b : u32;
+  b = atomicLoad(&(wg.a));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_NestedStruct) {
+    auto* src = R"(
+struct S0 {
+  a : u32,
+}
+
+struct S1 {
+  s0 : S0
+}
+
+var<workgroup> wg : S1;
+
+fn f() {
+  stub_atomicAdd_u32(wg.s0.a, 1u);
+
+  wg.s0.a = 0u;
+  let a = wg.s0.a;
+  var b : u32;
+  b = wg.s0.a;
+}
+)";
+
+    auto* expect = R"(
+struct S0_atomic {
+  a : atomic<u32>,
+}
+
+struct S0 {
+  a : u32,
+}
+
+struct S1_atomic {
+  s0 : S0_atomic,
+}
+
+struct S1 {
+  s0 : S0,
+}
+
+var<workgroup> wg : S1_atomic;
+
+fn f() {
+  atomicAdd(&(wg.s0.a), 1u);
+  atomicStore(&(wg.s0.a), 0u);
+  let a = atomicLoad(&(wg.s0.a));
+  var b : u32;
+  b = atomicLoad(&(wg.s0.a));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_StructMultipleAtomics) {
+    auto* src = R"(
+struct S {
+  a : u32,
+  b : u32,
+  c : u32,
+}
+
+var<workgroup> wg : S;
+
+fn f() {
+  stub_atomicAdd_u32(wg.a, 1u);
+  stub_atomicAdd_u32(wg.b, 1u);
+
+  wg.a = 0u;
+  let a = wg.a;
+  var b : u32;
+  b = wg.a;
+
+  wg.b = 0u;
+  let c = wg.b;
+  var d : u32;
+  d = wg.b;
+
+  wg.c = 0u;
+  let e = wg.c;
+  var f : u32;
+  f = wg.c;
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+  b : atomic<u32>,
+  c : u32,
+}
+
+struct S {
+  a : u32,
+  b : u32,
+  c : u32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f() {
+  atomicAdd(&(wg.a), 1u);
+  atomicAdd(&(wg.b), 1u);
+  atomicStore(&(wg.a), 0u);
+  let a = atomicLoad(&(wg.a));
+  var b : u32;
+  b = atomicLoad(&(wg.a));
+  atomicStore(&(wg.b), 0u);
+  let c = atomicLoad(&(wg.b));
+  var d : u32;
+  d = atomicLoad(&(wg.b));
+  wg.c = 0u;
+  let e = wg.c;
+  var f : u32;
+  f = wg.c;
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_ArrayOfScalar) {
+    auto* src = R"(
+var<workgroup> wg : array<u32, 4>;
+
+fn f() {
+  stub_atomicAdd_u32(wg[1], 1u);
+
+  wg[1] = 0u;
+  let a = wg[1];
+  var b : u32;
+  b = wg[1];
+}
+)";
+
+    auto* expect = R"(
+var<workgroup> wg : array<atomic<u32>, 4u>;
+
+fn f() {
+  atomicAdd(&(wg[1]), 1u);
+  atomicStore(&(wg[1]), 0u);
+  let a = atomicLoad(&(wg[1]));
+  var b : u32;
+  b = atomicLoad(&(wg[1]));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_ArrayOfStruct) {
+    auto* src = R"(
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : array<S, 4>;
+
+fn f() {
+  stub_atomicAdd_u32(wg[1].a, 1u);
+
+  wg[1].a = 0u;
+  let a = wg[1].a;
+  var b : u32;
+  b = wg[1].a;
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+}
+
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : array<S_atomic, 4u>;
+
+fn f() {
+  atomicAdd(&(wg[1].a), 1u);
+  atomicStore(&(wg[1].a), 0u);
+  let a = atomicLoad(&(wg[1].a));
+  var b : u32;
+  b = atomicLoad(&(wg[1].a));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_StructOfArray) {
+    auto* src = R"(
+struct S {
+  a : array<u32>,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  stub_atomicAdd_u32(s.a[4], 1u);
+
+  s.a[4] = 0u;
+  let a = s.a[4];
+  var b : u32;
+  b = s.a[4];
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : array<atomic<u32>>,
+}
+
+struct S {
+  a : array<u32>,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  atomicAdd(&(s.a[4]), 1u);
+  atomicStore(&(s.a[4]), 0u);
+  let a = atomicLoad(&(s.a[4]));
+  var b : u32;
+  b = atomicLoad(&(s.a[4]));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ReplaceAssignsAndDecls_ViaPtrLet) {
+    auto* src = R"(
+struct S {
+  i : u32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  let p0 = &(s);
+  let p1 : ptr<storage, u32, read_write> = &((*(p0)).i);
+  stub_atomicAdd_u32(*p1, 1u);
+
+  *p1 = 0u;
+  let a = *p1;
+  var b : u32;
+  b = *p1;
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  i : atomic<u32>,
+}
+
+struct S {
+  i : u32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  let p0 = &(s);
+  let p1 : ptr<storage, atomic<u32>, read_write> = &((*(p0)).i);
+  atomicAdd(&(*(p1)), 1u);
+  atomicStore(&(*(p1)), 0u);
+  let a = atomicLoad(&(*(p1)));
+  var b : u32;
+  b = atomicLoad(&(*(p1)));
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+}  // namespace
+}  // namespace tint::transform
diff --git a/src/tint/transform/unshadow.cc b/src/tint/transform/unshadow.cc
index 6f0292b..952a88b 100644
--- a/src/tint/transform/unshadow.cc
+++ b/src/tint/transform/unshadow.cc
@@ -63,6 +63,9 @@
                 [&](const ast::Let*) {
                     return ctx.dst->Let(source, symbol, type, constructor, attributes);
                 },
+                [&](const ast::Const*) {
+                    return ctx.dst->Const(source, symbol, type, constructor, attributes);
+                },
                 [&](const ast::Parameter*) {
                     return ctx.dst->Param(source, symbol, type, attributes);
                 },
diff --git a/src/tint/transform/unshadow_test.cc b/src/tint/transform/unshadow_test.cc
index 30e1db5..f5a8102 100644
--- a/src/tint/transform/unshadow_test.cc
+++ b/src/tint/transform/unshadow_test.cc
@@ -34,14 +34,16 @@
     auto* src = R"(
 var<private> a : i32;
 
-let b : i32 = 1;
+const b : i32 = 1;
 
 fn F(c : i32) {
   var d : i32;
   let e : i32 = 1;
+  const f : i32 = 2;
   {
-    var f : i32;
-    let g : i32 = 1;
+    var g : i32;
+    let h : i32 = 1;
+    const i : i32 = 2;
   }
 }
 )";
@@ -64,6 +66,10 @@
 fn Y() {
   let a = true;
 }
+
+fn Z() {
+  const a = true;
+}
 )";
 
     auto* expect = R"(
@@ -76,6 +82,10 @@
 fn Y() {
   let a_2 = true;
 }
+
+fn Z() {
+  const a_3 = true;
+}
 )";
 
     auto got = Run<Unshadow>(src);
@@ -93,6 +103,10 @@
   let a = true;
 }
 
+fn Z() {
+  const a = true;
+}
+
 type a = i32;
 )";
 
@@ -105,6 +119,10 @@
   let a_2 = true;
 }
 
+fn Z() {
+  const a_3 = true;
+}
+
 type a = i32;
 )";
 
@@ -126,6 +144,10 @@
 fn Y() {
   let a = false;
 }
+
+fn Z() {
+  const a = false;
+}
 )";
 
     auto* expect = R"(
@@ -140,6 +162,10 @@
 fn Y() {
   let a_2 = false;
 }
+
+fn Z() {
+  const a_3 = false;
+}
 )";
 
     auto got = Run<Unshadow>(src);
@@ -157,6 +183,10 @@
   let a = false;
 }
 
+fn Z() {
+  const a = false;
+}
+
 struct a {
   m : i32,
 };
@@ -172,6 +202,10 @@
   let a_2 = false;
 }
 
+fn Z() {
+  const a_3 = false;
+}
+
 struct a {
   m : i32,
 }
@@ -187,11 +221,19 @@
 fn a() {
   var a = true;
   var b = false;
+  var c = true;
 }
 
 fn b() {
   let a = true;
   let b = false;
+  let c = true;
+}
+
+fn c() {
+  const a = true;
+  const b = false;
+  const c = true;
 }
 )";
 
@@ -199,11 +241,19 @@
 fn a() {
   var a_1 = true;
   var b_1 = false;
+  var c_1 = true;
 }
 
 fn b() {
   let a_2 = true;
   let b_2 = false;
+  let c_2 = true;
+}
+
+fn c() {
+  const a_3 = true;
+  const b_3 = false;
+  const c_3 = true;
 }
 )";
 
@@ -217,24 +267,39 @@
 fn b() {
   let a = true;
   let b = false;
+  let c = true;
 }
 
 fn a() {
   var a = true;
   var b = false;
+  var c = true;
 }
 
+fn c() {
+  const a = true;
+  const b = false;
+  const c = true;
+}
 )";
 
     auto* expect = R"(
 fn b() {
   let a_1 = true;
   let b_1 = false;
+  let c_1 = true;
 }
 
 fn a() {
   var a_2 = true;
   var b_2 = false;
+  var c_2 = true;
+}
+
+fn c() {
+  const a_3 = true;
+  const b_3 = false;
+  const c_3 = true;
 }
 )";
 
@@ -254,6 +319,10 @@
 fn Y() {
   let a = (a == 321);
 }
+
+fn Z() {
+  const a = 321;
+}
 )";
 
     auto* expect = R"(
@@ -266,6 +335,10 @@
 fn Y() {
   let a_2 = (a == 321);
 }
+
+fn Z() {
+  const a_3 = 321;
+}
 )";
 
     auto got = Run<Unshadow>(src);
@@ -283,6 +356,10 @@
   let a = (a == 321);
 }
 
+fn Z() {
+  const a = 321;
+}
+
 var<private> a : i32;
 )";
 
@@ -295,6 +372,10 @@
   let a_2 = (a == 321);
 }
 
+fn Z() {
+  const a_3 = 321;
+}
+
 var<private> a : i32;
 )";
 
@@ -314,10 +395,14 @@
 fn Y() {
   let a = (a == 321);
 }
+
+fn Z() {
+  const a = 321;
+}
 )";
 
     auto* expect = R"(
-let a : i32 = 1;
+const a : i32 = 1;
 
 fn X() {
   var a_1 = (a == 123);
@@ -326,6 +411,10 @@
 fn Y() {
   let a_2 = (a == 321);
 }
+
+fn Z() {
+  const a_3 = 321;
+}
 )";
 
     auto got = Run<Unshadow>(src);
@@ -343,6 +432,10 @@
   let a = (a == 321);
 }
 
+fn Z() {
+  const a = 321;
+}
+
 let a : i32 = 1;
 )";
 
@@ -355,7 +448,87 @@
   let a_2 = (a == 321);
 }
 
-let a : i32 = 1;
+fn Z() {
+  const a_3 = 321;
+}
+
+const a : i32 = 1;
+)";
+
+    auto got = Run<Unshadow>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(UnshadowTest, LocalShadowsGlobalConst) {
+    auto* src = R"(
+const a : i32 = 1;
+
+fn X() {
+  var a = (a == 123);
+}
+
+fn Y() {
+  let a = (a == 321);
+}
+
+fn Z() {
+  const a = 321;
+}
+)";
+
+    auto* expect = R"(
+const a : i32 = 1;
+
+fn X() {
+  var a_1 = (a == 123);
+}
+
+fn Y() {
+  let a_2 = (a == 321);
+}
+
+fn Z() {
+  const a_3 = 321;
+}
+)";
+
+    auto got = Run<Unshadow>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(UnshadowTest, LocalShadowsGlobalConst_OutOfOrder) {
+    auto* src = R"(
+fn X() {
+  var a = (a == 123);
+}
+
+fn Y() {
+  let a = (a == 321);
+}
+
+fn Z() {
+  const a = a;
+}
+
+const a : i32 = 1;
+)";
+
+    auto* expect = R"(
+fn X() {
+  var a_1 = (a == 123);
+}
+
+fn Y() {
+  let a_2 = (a == 321);
+}
+
+fn Z() {
+  const a_3 = a;
+}
+
+const a : i32 = 1;
 )";
 
     auto got = Run<Unshadow>(src);
@@ -373,6 +546,9 @@
   {
     let a = (a == 321);
   }
+  {
+    const a = 321;
+  }
 }
 )";
 
@@ -385,6 +561,9 @@
   {
     let a_2 = (a == 321);
   }
+  {
+    const a_3 = 321;
+  }
 }
 )";
 
@@ -403,6 +582,9 @@
   {
     let a = (a == 321);
   }
+  {
+    const a = 321;
+  }
 }
 )";
 
@@ -415,6 +597,45 @@
   {
     let a_2 = (a == 321);
   }
+  {
+    const a_3 = 321;
+  }
+}
+)";
+
+    auto got = Run<Unshadow>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(UnshadowTest, LocalShadowsLocalConst) {
+    auto* src = R"(
+fn X() {
+  const a = 1;
+  {
+    var a = (a == 123);
+  }
+  {
+    let a = (a == 321);
+  }
+  {
+    const a = a;
+  }
+}
+)";
+
+    auto* expect = R"(
+fn X() {
+  const a = 1;
+  {
+    var a_1 = (a == 123);
+  }
+  {
+    let a_2 = (a == 321);
+  }
+  {
+    const a_3 = a;
+  }
 }
 )";
 
@@ -432,6 +653,9 @@
   {
     let a = (a == 321);
   }
+  {
+    const a = 321;
+  }
 }
 )";
 
@@ -443,6 +667,9 @@
   {
     let a_2 = (a == 321);
   }
+  {
+    const a_3 = 321;
+  }
 }
 )";
 
@@ -460,6 +687,9 @@
   {
     let a = (a == 321);
   }
+  {
+    const a = 321;
+  }
 }
 )";
 
@@ -471,6 +701,9 @@
   {
     let a_3 = (a_1 == 321);
   }
+  {
+    const a_4 = 321;
+  }
 }
 )";
 
@@ -508,7 +741,7 @@
 )";
 
     auto* expect = R"(
-let a : i32 = 1;
+const a : i32 = 1;
 
 fn F(a_1 : bool) {
 }
@@ -531,7 +764,47 @@
 fn F(a_1 : bool) {
 }
 
-let a : i32 = 1;
+const a : i32 = 1;
+)";
+
+    auto got = Run<Unshadow>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(UnshadowTest, ParamShadowsGlobalConst) {
+    auto* src = R"(
+const a : i32 = 1;
+
+fn F(a : bool) {
+}
+)";
+
+    auto* expect = R"(
+const a : i32 = 1;
+
+fn F(a_1 : bool) {
+}
+)";
+
+    auto got = Run<Unshadow>(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(UnshadowTest, ParamShadowsGlobalConst_OutOfOrder) {
+    auto* src = R"(
+fn F(a : bool) {
+}
+
+const a : i32 = 1;
+)";
+
+    auto* expect = R"(
+fn F(a_1 : bool) {
+}
+
+const a : i32 = 1;
 )";
 
     auto got = Run<Unshadow>(src);
diff --git a/src/tint/transform/unwind_discard_functions.cc b/src/tint/transform/unwind_discard_functions.cc
index a7877e1..d41ef6c 100644
--- a/src/tint/transform/unwind_discard_functions.cc
+++ b/src/tint/transform/unwind_discard_functions.cc
@@ -54,8 +54,8 @@
     Symbol ModuleDiscardVarName() {
         if (!module_discard_var_name.IsValid()) {
             module_discard_var_name = b.Symbols().New("tint_discard");
-            ctx.dst->Global(module_discard_var_name, b.ty.bool_(), b.Expr(false),
-                            ast::StorageClass::kPrivate);
+            ctx.dst->GlobalVar(module_discard_var_name, b.ty.bool_(), b.Expr(false),
+                               ast::StorageClass::kPrivate);
         }
         return module_discard_var_name;
     }
diff --git a/src/tint/transform/utils/hoist_to_decl_before.cc b/src/tint/transform/utils/hoist_to_decl_before.cc
index 41f2287..2995522 100644
--- a/src/tint/transform/utils/hoist_to_decl_before.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before.cc
@@ -16,7 +16,7 @@
 
 #include <unordered_map>
 
-#include "src/tint/ast/variable_decl_statement.h"
+#include "src/tint/program_builder.h"
 #include "src/tint/sem/block_statement.h"
 #include "src/tint/sem/for_loop_statement.h"
 #include "src/tint/sem/if_statement.h"
diff --git a/src/tint/transform/utils/hoist_to_decl_before_test.cc b/src/tint/transform/utils/hoist_to_decl_before_test.cc
index 46d5551..22784ba 100644
--- a/src/tint/transform/utils/hoist_to_decl_before_test.cc
+++ b/src/tint/transform/utils/hoist_to_decl_before_test.cc
@@ -17,6 +17,7 @@
 #include "gtest/gtest-spi.h"
 #include "src/tint/program_builder.h"
 #include "src/tint/sem/if_statement.h"
+#include "src/tint/sem/index_accessor_expression.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/transform/test_helper.h"
 #include "src/tint/transform/utils/hoist_to_decl_before.h"
diff --git a/src/tint/transform/var_for_dynamic_index_test.cc b/src/tint/transform/var_for_dynamic_index_test.cc
index ca767c9..05c19b3 100644
--- a/src/tint/transform/var_for_dynamic_index_test.cc
+++ b/src/tint/transform/var_for_dynamic_index_test.cc
@@ -484,7 +484,8 @@
 fn f() {
   let p = array<i32, 4>(1, 2, 3, 4);
   let c = 1;
-  let x = p[c];
+  var var_for_index = p;
+  let x = var_for_index[c];
 }
 )";
 
@@ -501,7 +502,8 @@
 fn f() {
   let p = mat2x2(1.0, 2.0, 3.0, 4.0);
   let c = 1;
-  let x = p[c];
+  var var_for_index = p;
+  let x = var_for_index[c];
 }
 )";
 
diff --git a/src/tint/transform/vertex_pulling.cc b/src/tint/transform/vertex_pulling.cc
index a1627e2..50b8f28 100644
--- a/src/tint/transform/vertex_pulling.cc
+++ b/src/tint/transform/vertex_pulling.cc
@@ -259,12 +259,12 @@
                                });
         for (uint32_t i = 0; i < cfg.vertex_state.size(); ++i) {
             // The decorated variable with struct type
-            ctx.dst->Global(GetVertexBufferName(i), ctx.dst->ty.Of(struct_type),
-                            ast::StorageClass::kStorage, ast::Access::kRead,
-                            ast::AttributeList{
-                                ctx.dst->create<ast::BindingAttribute>(i),
-                                ctx.dst->create<ast::GroupAttribute>(cfg.pulling_group),
-                            });
+            ctx.dst->GlobalVar(GetVertexBufferName(i), ctx.dst->ty.Of(struct_type),
+                               ast::StorageClass::kStorage, ast::Access::kRead,
+                               ast::AttributeList{
+                                   ctx.dst->create<ast::BindingAttribute>(i),
+                                   ctx.dst->create<ast::GroupAttribute>(cfg.pulling_group),
+                               });
         }
     }
 
diff --git a/src/tint/transform/wrap_arrays_in_structs.cc b/src/tint/transform/wrap_arrays_in_structs.cc
deleted file mode 100644
index eb133d7..0000000
--- a/src/tint/transform/wrap_arrays_in_structs.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2021 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/transform/wrap_arrays_in_structs.h"
-
-#include <utility>
-
-#include "src/tint/program_builder.h"
-#include "src/tint/sem/array.h"
-#include "src/tint/sem/call.h"
-#include "src/tint/sem/expression.h"
-#include "src/tint/sem/type_constructor.h"
-#include "src/tint/utils/map.h"
-#include "src/tint/utils/transform.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::transform::WrapArraysInStructs);
-
-namespace tint::transform {
-
-WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo() = default;
-WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo(const WrappedArrayInfo&) = default;
-WrapArraysInStructs::WrappedArrayInfo::~WrappedArrayInfo() = default;
-
-WrapArraysInStructs::WrapArraysInStructs() = default;
-
-WrapArraysInStructs::~WrapArraysInStructs() = default;
-
-bool WrapArraysInStructs::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (program->Sem().Get<sem::Array>(node->As<ast::Type>())) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void WrapArraysInStructs::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
-
-    std::unordered_map<const sem::Array*, WrappedArrayInfo> wrapped_arrays;
-    auto wrapper = [&](const sem::Array* array) { return WrapArray(ctx, wrapped_arrays, array); };
-    auto wrapper_typename = [&](const sem::Array* arr) -> ast::TypeName* {
-        auto info = wrapper(arr);
-        return info ? ctx.dst->create<ast::TypeName>(info.wrapper_name) : nullptr;
-    };
-
-    // Replace all array types with their corresponding wrapper
-    ctx.ReplaceAll([&](const ast::Type* ast_type) -> const ast::Type* {
-        auto* type = ctx.src->TypeOf(ast_type);
-        if (auto* array = type->UnwrapRef()->As<sem::Array>()) {
-            return wrapper_typename(array);
-        }
-        return nullptr;
-    });
-
-    // Fix up index accessors so `a[1]` becomes `a.arr[1]`
-    ctx.ReplaceAll(
-        [&](const ast::IndexAccessorExpression* accessor) -> const ast::IndexAccessorExpression* {
-            if (auto* array =
-                    ::tint::As<sem::Array>(sem.Get(accessor->object)->Type()->UnwrapRef())) {
-                if (wrapper(array)) {
-                    // Array is wrapped in a structure. Emit a member accessor to get
-                    // to the actual array.
-                    auto* arr = ctx.Clone(accessor->object);
-                    auto* idx = ctx.Clone(accessor->index);
-                    auto* unwrapped = ctx.dst->MemberAccessor(arr, "arr");
-                    return ctx.dst->IndexAccessor(accessor->source, unwrapped, idx);
-                }
-            }
-            return nullptr;
-        });
-
-    // Fix up array constructors so `A(1,2)` becomes `tint_array_wrapper(A(1,2))`
-    ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::Expression* {
-        if (auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>()) {
-            if (auto* ctor = call->Target()->As<sem::TypeConstructor>()) {
-                if (auto* array = ctor->ReturnType()->As<sem::Array>()) {
-                    if (auto w = wrapper(array)) {
-                        // Wrap the array type constructor with another constructor for
-                        // the wrapper
-                        auto* wrapped_array_ty = ctx.dst->ty.type_name(w.wrapper_name);
-                        auto* array_ty = w.array_type(ctx);
-                        auto args = utils::Transform(call->Arguments(),
-                                                     [&](const tint::sem::Expression* s) {
-                                                         return ctx.Clone(s->Declaration());
-                                                     });
-                        auto* arr_ctor = ctx.dst->Construct(array_ty, args);
-                        return ctx.dst->Construct(wrapped_array_ty, arr_ctor);
-                    }
-                }
-            }
-        }
-        return nullptr;
-    });
-
-    ctx.Clone();
-}
-
-WrapArraysInStructs::WrappedArrayInfo WrapArraysInStructs::WrapArray(
-    CloneContext& ctx,
-    std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
-    const sem::Array* array) const {
-    if (array->IsRuntimeSized()) {
-        return {};  // We don't want to wrap runtime sized arrays
-    }
-
-    return utils::GetOrCreate(wrapped_arrays, array, [&] {
-        WrappedArrayInfo info;
-
-        // Generate a unique name for the array wrapper
-        info.wrapper_name = ctx.dst->Symbols().New("tint_array_wrapper");
-
-        // Examine the element type. Is it also an array?
-        std::function<const ast::Type*(CloneContext&)> el_type;
-        if (auto* el_array = array->ElemType()->As<sem::Array>()) {
-            // Array of array - call WrapArray() on the element type
-            if (auto el = WrapArray(ctx, wrapped_arrays, el_array)) {
-                el_type = [=](CloneContext& c) {
-                    return c.dst->create<ast::TypeName>(el.wrapper_name);
-                };
-            }
-        }
-
-        // If the element wasn't an array, just create the typical AST type for it
-        if (!el_type) {
-            el_type = [=](CloneContext& c) { return CreateASTTypeFor(c, array->ElemType()); };
-        }
-
-        // Construct the single structure field type
-        info.array_type = [=](CloneContext& c) {
-            ast::AttributeList attrs;
-            if (!array->IsStrideImplicit()) {
-                attrs.emplace_back(c.dst->create<ast::StrideAttribute>(array->Stride()));
-            }
-            return c.dst->ty.array(el_type(c), u32(array->Count()), std::move(attrs));
-        };
-
-        // Structure() will create and append the ast::Struct to the
-        // global declarations of `ctx.dst`. As we haven't finished building the
-        // current module-scope statement or function, this will be placed
-        // immediately before the usage.
-        ctx.dst->Structure(info.wrapper_name, {ctx.dst->Member("arr", info.array_type(ctx))});
-        return info;
-    });
-}
-
-}  // namespace tint::transform
diff --git a/src/tint/transform/wrap_arrays_in_structs.h b/src/tint/transform/wrap_arrays_in_structs.h
deleted file mode 100644
index 4653c6b..0000000
--- a/src/tint/transform/wrap_arrays_in_structs.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2021 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_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
-#define SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/transform/transform.h"
-
-// Forward declarations
-namespace tint::ast {
-class Type;
-}  // namespace tint::ast
-
-namespace tint::transform {
-
-/// WrapArraysInStructs is a transform that replaces all array types with a
-/// structure holding a single field of that array type.
-/// Array index expressions and constructors are also adjusted to deal with this
-/// wrapping.
-/// This transform helps with backends that cannot directly return arrays or use
-/// them as parameters.
-class WrapArraysInStructs : public Castable<WrapArraysInStructs, Transform> {
-  public:
-    /// Constructor
-    WrapArraysInStructs();
-
-    /// Destructor
-    ~WrapArraysInStructs() override;
-
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
-
-  private:
-    struct WrappedArrayInfo {
-        WrappedArrayInfo();
-        WrappedArrayInfo(const WrappedArrayInfo&);
-        ~WrappedArrayInfo();
-
-        Symbol wrapper_name;
-        std::function<const ast::Type*(CloneContext&)> array_type;
-
-        operator bool() { return wrapper_name.IsValid(); }
-    };
-
-    /// WrapArray wraps the fixed-size array type in a new structure (if it hasn't
-    /// already been wrapped). WrapArray will recursively wrap arrays-of-arrays.
-    /// The new structure will be added to module-scope type declarations of
-    /// `ctx.dst`.
-    /// @param ctx the CloneContext
-    /// @param wrapped_arrays a map of src array type to the wrapped structure
-    /// name
-    /// @param array the array type
-    /// @return the name of the structure that wraps the array, or an invalid
-    /// Symbol if this array should not be wrapped
-    WrappedArrayInfo WrapArray(
-        CloneContext& ctx,
-        std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
-        const sem::Array* array) const;
-};
-
-}  // namespace tint::transform
-
-#endif  // SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
diff --git a/src/tint/transform/wrap_arrays_in_structs_test.cc b/src/tint/transform/wrap_arrays_in_structs_test.cc
deleted file mode 100644
index 7a7a6b3..0000000
--- a/src/tint/transform/wrap_arrays_in_structs_test.cc
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright 2021 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/transform/wrap_arrays_in_structs.h"
-
-#include <memory>
-#include <utility>
-
-#include "src/tint/transform/test_helper.h"
-
-namespace tint::transform {
-namespace {
-
-using WrapArraysInStructsTest = TransformTest;
-
-TEST_F(WrapArraysInStructsTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<WrapArraysInStructs>(src));
-}
-
-TEST_F(WrapArraysInStructsTest, ShouldRunHasArray) {
-    auto* src = R"(
-var<private> arr : array<i32, 4>;
-)";
-
-    EXPECT_TRUE(ShouldRun<WrapArraysInStructs>(src));
-}
-
-TEST_F(WrapArraysInStructsTest, EmptyModule) {
-    auto* src = R"()";
-    auto* expect = src;
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsGlobal) {
-    auto* src = R"(
-var<private> arr : array<i32, 4>;
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-var<private> arr : tint_array_wrapper;
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsFunctionVar) {
-    auto* src = R"(
-fn f() {
-  var arr : array<i32, 4>;
-  let x = arr[3];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-fn f() {
-  var arr : tint_array_wrapper;
-  let x = arr.arr[3];
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsParam) {
-    auto* src = R"(
-fn f(a : array<i32, 4>) -> i32 {
-  return a[2];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-fn f(a : tint_array_wrapper) -> i32 {
-  return a.arr[2];
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsReturn) {
-    auto* src = R"(
-fn f() -> array<i32, 4> {
-  return array<i32, 4>(1, 2, 3, 4);
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-fn f() -> tint_array_wrapper {
-  return tint_array_wrapper(array<i32, 4u>(1, 2, 3, 4));
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAlias) {
-    auto* src = R"(
-type Inner = array<i32, 2>;
-type Array = array<Inner, 2>;
-
-fn f() {
-  var arr : Array;
-  arr = Array();
-  arr = Array(Inner(1, 2), Inner(3, 4));
-  let vals : Array = Array(Inner(1, 2), Inner(3, 4));
-  arr = vals;
-  let x = arr[3];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 2u>,
-}
-
-type Inner = tint_array_wrapper;
-
-struct tint_array_wrapper_1 {
-  arr : array<tint_array_wrapper, 2u>,
-}
-
-type Array = tint_array_wrapper_1;
-
-fn f() {
-  var arr : tint_array_wrapper_1;
-  arr = tint_array_wrapper_1(array<tint_array_wrapper, 2u>());
-  arr = tint_array_wrapper_1(array<tint_array_wrapper, 2u>(tint_array_wrapper(array<i32, 2u>(1, 2)), tint_array_wrapper(array<i32, 2u>(3, 4))));
-  let vals : tint_array_wrapper_1 = tint_array_wrapper_1(array<tint_array_wrapper, 2u>(tint_array_wrapper(array<i32, 2u>(1, 2)), tint_array_wrapper(array<i32, 2u>(3, 4))));
-  arr = vals;
-  let x = arr.arr[3];
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAlias_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var arr : Array;
-  arr = Array();
-  arr = Array(Inner(1, 2), Inner(3, 4));
-  let vals : Array = Array(Inner(1, 2), Inner(3, 4));
-  arr = vals;
-  let x = arr[3];
-}
-
-type Array = array<Inner, 2>;
-type Inner = array<i32, 2>;
-)";
-    auto* expect = R"(
-struct tint_array_wrapper_1 {
-  arr : array<i32, 2u>,
-}
-
-struct tint_array_wrapper {
-  arr : array<tint_array_wrapper_1, 2u>,
-}
-
-fn f() {
-  var arr : tint_array_wrapper;
-  arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>());
-  arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
-  let vals : tint_array_wrapper = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
-  arr = vals;
-  let x = arr.arr[3];
-}
-
-type Array = tint_array_wrapper;
-
-type Inner = tint_array_wrapper_1;
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArraysInStruct) {
-    auto* src = R"(
-struct S {
-  a : array<i32, 4>,
-  b : array<i32, 8>,
-  c : array<i32, 4>,
-};
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-struct tint_array_wrapper_1 {
-  arr : array<i32, 8u>,
-}
-
-struct S {
-  a : tint_array_wrapper,
-  b : tint_array_wrapper_1,
-  c : tint_array_wrapper,
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArraysOfArraysInStruct) {
-    auto* src = R"(
-struct S {
-  a : array<i32, 4>,
-  b : array<array<i32, 4>, 4>,
-  c : array<array<array<i32, 4>, 4>, 4>,
-};
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-struct tint_array_wrapper_1 {
-  arr : array<tint_array_wrapper, 4u>,
-}
-
-struct tint_array_wrapper_2 {
-  arr : array<tint_array_wrapper_1, 4u>,
-}
-
-struct S {
-  a : tint_array_wrapper,
-  b : tint_array_wrapper_1,
-  c : tint_array_wrapper_2,
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, AccessArraysOfArraysInStruct) {
-    auto* src = R"(
-struct S {
-  a : array<i32, 4>,
-  b : array<array<i32, 4>, 4>,
-  c : array<array<array<i32, 4>, 4>, 4>,
-};
-
-fn f(s : S) -> i32 {
-  return s.a[2] + s.b[1][2] + s.c[3][1][2];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-struct tint_array_wrapper_1 {
-  arr : array<tint_array_wrapper, 4u>,
-}
-
-struct tint_array_wrapper_2 {
-  arr : array<tint_array_wrapper_1, 4u>,
-}
-
-struct S {
-  a : tint_array_wrapper,
-  b : tint_array_wrapper_1,
-  c : tint_array_wrapper_2,
-}
-
-fn f(s : S) -> i32 {
-  return ((s.a.arr[2] + s.b.arr[1].arr[2]) + s.c.arr[3].arr[1].arr[2]);
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, DeclarationOrder) {
-    auto* src = R"(
-type T0 = i32;
-
-type T1 = array<i32, 1>;
-
-type T2 = i32;
-
-fn f1(a : array<i32, 2>) {
-}
-
-type T3 = i32;
-
-fn f2() {
-  var v : array<i32, 3>;
-}
-)";
-    auto* expect = R"(
-type T0 = i32;
-
-struct tint_array_wrapper {
-  arr : array<i32, 1u>,
-}
-
-type T1 = tint_array_wrapper;
-
-type T2 = i32;
-
-struct tint_array_wrapper_1 {
-  arr : array<i32, 2u>,
-}
-
-fn f1(a : tint_array_wrapper_1) {
-}
-
-type T3 = i32;
-
-struct tint_array_wrapper_2 {
-  arr : array<i32, 3u>,
-}
-
-fn f2() {
-  var v : tint_array_wrapper_2;
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, DeclarationOrder_OutOfOrder) {
-    auto* src = R"(
-fn f2() {
-  var v : array<i32, 3>;
-}
-
-type T3 = i32;
-
-fn f1(a : array<i32, 2>) {
-}
-
-type T2 = i32;
-
-type T1 = array<i32, 1>;
-
-type T0 = i32;
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 3u>,
-}
-
-fn f2() {
-  var v : tint_array_wrapper;
-}
-
-type T3 = i32;
-
-struct tint_array_wrapper_1 {
-  arr : array<i32, 2u>,
-}
-
-fn f1(a : tint_array_wrapper_1) {
-}
-
-type T2 = i32;
-
-struct tint_array_wrapper_2 {
-  arr : array<i32, 1u>,
-}
-
-type T1 = tint_array_wrapper_2;
-
-type T0 = i32;
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::transform
diff --git a/src/tint/transform/zero_init_workgroup_memory.cc b/src/tint/transform/zero_init_workgroup_memory.cc
index f56dc61..96395f1 100644
--- a/src/tint/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/transform/zero_init_workgroup_memory.cc
@@ -358,8 +358,8 @@
                 continue;
             }
             auto* sem = ctx.src->Sem().Get(expr);
-            if (auto c = sem->ConstantValue()) {
-                workgroup_size_const *= c.Element<AInt>(0).value;
+            if (auto* c = sem->ConstantValue()) {
+                workgroup_size_const *= c->As<AInt>();
                 continue;
             }
             // Constant value could not be found. Build expression instead.
diff --git a/src/tint/utils/block_allocator_test.cc b/src/tint/utils/block_allocator_test.cc
index 600019c..77ea3e3 100644
--- a/src/tint/utils/block_allocator_test.cc
+++ b/src/tint/utils/block_allocator_test.cc
@@ -67,7 +67,7 @@
 TEST_F(BlockAllocatorTest, MoveConstruct) {
     using Allocator = BlockAllocator<LifetimeCounter>;
 
-    for (size_t n : {0, 1, 10, 16, 20, 32, 50, 64, 100, 256, 300, 512, 500, 512}) {
+    for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
         size_t count = 0;
         {
             Allocator allocator_a;
@@ -87,7 +87,7 @@
 TEST_F(BlockAllocatorTest, MoveAssign) {
     using Allocator = BlockAllocator<LifetimeCounter>;
 
-    for (size_t n : {0, 1, 10, 16, 20, 32, 50, 64, 100, 256, 300, 512, 500, 512}) {
+    for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
         size_t count_a = 0;
         size_t count_b = 0;
 
diff --git a/src/tint/utils/compiler_macros.h b/src/tint/utils/compiler_macros.h
index 34965c6..b3cca3c 100644
--- a/src/tint/utils/compiler_macros.h
+++ b/src/tint/utils/compiler_macros.h
@@ -25,7 +25,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 #define TINT_DISABLE_WARNING_CONSTANT_OVERFLOW __pragma(warning(disable : 4756))
 #define TINT_DISABLE_WARNING_MAYBE_UNINITIALIZED /* currently no-op */
+#define TINT_DISABLE_WARNING_NEWLINE_EOF         /* currently no-op */
+#define TINT_DISABLE_WARNING_OLD_STYLE_CAST      /* currently no-op */
+#define TINT_DISABLE_WARNING_SIGN_CONVERSION     /* currently no-op */
 #define TINT_DISABLE_WARNING_UNREACHABLE_CODE __pragma(warning(disable : 4702))
+#define TINT_DISABLE_WARNING_WEAK_VTABLES /* currently no-op */
 
 // clang-format off
 #define TINT_BEGIN_DISABLE_WARNING(name)     \
@@ -42,7 +46,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 #define TINT_DISABLE_WARNING_CONSTANT_OVERFLOW   /* currently no-op */
 #define TINT_DISABLE_WARNING_MAYBE_UNINITIALIZED /* currently no-op */
+#define TINT_DISABLE_WARNING_NEWLINE_EOF _Pragma("clang diagnostic ignored \"-Wnewline-eof\"")
+#define TINT_DISABLE_WARNING_OLD_STYLE_CAST _Pragma("clang diagnostic ignored \"-Wold-style-cast\"")
+#define TINT_DISABLE_WARNING_SIGN_CONVERSION \
+    _Pragma("clang diagnostic ignored \"-Wsign-conversion\"")
 #define TINT_DISABLE_WARNING_UNREACHABLE_CODE    /* currently no-op */
+#define TINT_DISABLE_WARNING_WEAK_VTABLES _Pragma("clang diagnostic ignored \"-Wweak-vtables\"")
 
 // clang-format off
 #define TINT_BEGIN_DISABLE_WARNING(name)     \
@@ -60,7 +69,11 @@
 #define TINT_DISABLE_WARNING_CONSTANT_OVERFLOW /* currently no-op */
 #define TINT_DISABLE_WARNING_MAYBE_UNINITIALIZED \
     _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+#define TINT_DISABLE_WARNING_NEWLINE_EOF      /* currently no-op */
+#define TINT_DISABLE_WARNING_OLD_STYLE_CAST   /* currently no-op */
+#define TINT_DISABLE_WARNING_SIGN_CONVERSION  /* currently no-op */
 #define TINT_DISABLE_WARNING_UNREACHABLE_CODE /* currently no-op */
+#define TINT_DISABLE_WARNING_WEAK_VTABLES     /* currently no-op */
 
 // clang-format off
 #define TINT_BEGIN_DISABLE_WARNING(name)     \
diff --git a/src/tint/writer/append_vector.cc b/src/tint/writer/append_vector.cc
index bc89d1f..c5f184d 100644
--- a/src/tint/writer/append_vector.cc
+++ b/src/tint/writer/append_vector.cc
@@ -59,7 +59,7 @@
             << "unsupported vector element type: " << ty->TypeInfo().name;
         return nullptr;
     }
-    auto* sem = b.create<sem::Expression>(expr, ty, stmt, sem::Constant{},
+    auto* sem = b.create<sem::Expression>(expr, ty, stmt, /* constant_value */ nullptr,
                                           /* has_side_effects */ false);
     b.Sem().Add(expr, sem);
     return sem;
@@ -135,11 +135,11 @@
         auto* scalar_cast_ast = b->Construct(packed_el_ast_ty, scalar_ast);
         auto* scalar_cast_target = b->create<sem::TypeConversion>(
             packed_el_sem_ty,
-            b->create<sem::Parameter>(nullptr, 0, scalar_sem->Type()->UnwrapRef(),
+            b->create<sem::Parameter>(nullptr, 0u, scalar_sem->Type()->UnwrapRef(),
                                       ast::StorageClass::kNone, ast::Access::kUndefined));
         auto* scalar_cast_sem = b->create<sem::Call>(
             scalar_cast_ast, scalar_cast_target, std::vector<const sem::Expression*>{scalar_sem},
-            statement, sem::Constant{}, /* has_side_effects */ false);
+            statement, /* constant_value */ nullptr, /* has_side_effects */ false);
         b->Sem().Add(scalar_cast_ast, scalar_cast_sem);
         packed.emplace_back(scalar_cast_sem);
     } else {
@@ -158,7 +158,7 @@
                                                  ast::Access::kUndefined);
             }));
     auto* constructor_sem = b->create<sem::Call>(constructor_ast, constructor_target, packed,
-                                                 statement, sem::Constant{},
+                                                 statement, /* constant_value */ nullptr,
                                                  /* has_side_effects */ false);
     b->Sem().Add(constructor_ast, constructor_sem);
     return constructor_sem;
diff --git a/src/tint/writer/append_vector_test.cc b/src/tint/writer/append_vector_test.cc
index 8169039..2231003 100644
--- a/src/tint/writer/append_vector_test.cc
+++ b/src/tint/writer/append_vector_test.cc
@@ -250,7 +250,7 @@
 
 // AppendVector(vec_12, 3) -> vec3<i32>(vec_12, 3)
 TEST_F(AppendVectorTest, Vec2i32Var_i32) {
-    Global("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr(3_i);
     WrapInFunction(vec_12, scalar_3);
@@ -286,7 +286,7 @@
 
 // AppendVector(1, 2, scalar_3) -> vec3<i32>(1, 2, scalar_3)
 TEST_F(AppendVectorTest, Vec2i32_i32Var) {
-    Global("scalar_3", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("scalar_3", ty.i32(), ast::StorageClass::kPrivate);
     auto* scalar_1 = Expr(1_i);
     auto* scalar_2 = Expr(2_i);
     auto* scalar_3 = Expr("scalar_3");
@@ -327,8 +327,8 @@
 
 // AppendVector(vec_12, scalar_3) -> vec3<i32>(vec_12, scalar_3)
 TEST_F(AppendVectorTest, Vec2i32Var_i32Var) {
-    Global("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    Global("scalar_3", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("scalar_3", ty.i32(), ast::StorageClass::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
@@ -364,8 +364,8 @@
 
 // AppendVector(vec_12, scalar_3) -> vec3<i32>(vec_12, i32(scalar_3))
 TEST_F(AppendVectorTest, Vec2i32Var_f32Var) {
-    Global("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    Global("scalar_3", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("scalar_3", ty.f32(), ast::StorageClass::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
@@ -405,8 +405,8 @@
 
 // AppendVector(vec_12, scalar_3) -> vec3<bool>(vec_12, scalar_3)
 TEST_F(AppendVectorTest, Vec2boolVar_boolVar) {
-    Global("vec_12", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    Global("scalar_3", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("vec_12", ty.vec2<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("scalar_3", ty.bool_(), ast::StorageClass::kPrivate);
     auto* vec_12 = Expr("vec_12");
     auto* scalar_3 = Expr("scalar_3");
     WrapInFunction(vec_12, scalar_3);
diff --git a/src/tint/writer/flatten_bindings_test.cc b/src/tint/writer/flatten_bindings_test.cc
index 1c516c9..ae01abd 100644
--- a/src/tint/writer/flatten_bindings_test.cc
+++ b/src/tint/writer/flatten_bindings_test.cc
@@ -41,9 +41,9 @@
 
 TEST_F(FlattenBindingsTest, AlreadyFlat) {
     ProgramBuilder b;
-    b.Global("a", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
-    b.Global("b", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 1));
-    b.Global("c", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 2));
+    b.GlobalVar("a", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("b", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 1));
+    b.GlobalVar("c", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 2));
     b.WrapInFunction();
 
     resolver::Resolver resolver(&b);
@@ -57,9 +57,9 @@
 
 TEST_F(FlattenBindingsTest, NotFlat_SingleNamespace) {
     ProgramBuilder b;
-    b.Global("a", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
-    b.Global("b", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(1, 1));
-    b.Global("c", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(2, 2));
+    b.GlobalVar("a", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("b", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(1, 1));
+    b.GlobalVar("c", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(2, 2));
     b.WrapInFunction(b.Expr("a"), b.Expr("b"), b.Expr("c"));
 
     resolver::Resolver resolver(&b);
@@ -83,29 +83,30 @@
     ProgramBuilder b;
 
     const size_t num_buffers = 3;
-    b.Global("buffer1", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
-    b.Global("buffer2", b.ty.i32(), ast::StorageClass::kStorage, b.GroupAndBinding(1, 1));
-    b.Global("buffer3", b.ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
-             b.GroupAndBinding(2, 2));
+    b.GlobalVar("buffer1", b.ty.i32(), ast::StorageClass::kUniform, b.GroupAndBinding(0, 0));
+    b.GlobalVar("buffer2", b.ty.i32(), ast::StorageClass::kStorage, b.GroupAndBinding(1, 1));
+    b.GlobalVar("buffer3", b.ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+                b.GroupAndBinding(2, 2));
 
     const size_t num_samplers = 2;
-    b.Global("sampler1", b.ty.sampler(ast::SamplerKind::kSampler), b.GroupAndBinding(3, 3));
-    b.Global("sampler2", b.ty.sampler(ast::SamplerKind::kComparisonSampler),
-             b.GroupAndBinding(4, 4));
+    b.GlobalVar("sampler1", b.ty.sampler(ast::SamplerKind::kSampler), b.GroupAndBinding(3, 3));
+    b.GlobalVar("sampler2", b.ty.sampler(ast::SamplerKind::kComparisonSampler),
+                b.GroupAndBinding(4, 4));
 
     const size_t num_textures = 6;
-    b.Global("texture1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
-             b.GroupAndBinding(5, 5));
-    b.Global("texture2", b.ty.multisampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
-             b.GroupAndBinding(6, 6));
-    b.Global("texture3",
-             b.ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
-                                  ast::Access::kWrite),
-             b.GroupAndBinding(7, 7));
-    b.Global("texture4", b.ty.depth_texture(ast::TextureDimension::k2d), b.GroupAndBinding(8, 8));
-    b.Global("texture5", b.ty.depth_multisampled_texture(ast::TextureDimension::k2d),
-             b.GroupAndBinding(9, 9));
-    b.Global("texture6", b.ty.external_texture(), b.GroupAndBinding(10, 10));
+    b.GlobalVar("texture1", b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
+                b.GroupAndBinding(5, 5));
+    b.GlobalVar("texture2", b.ty.multisampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
+                b.GroupAndBinding(6, 6));
+    b.GlobalVar("texture3",
+                b.ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
+                                     ast::Access::kWrite),
+                b.GroupAndBinding(7, 7));
+    b.GlobalVar("texture4", b.ty.depth_texture(ast::TextureDimension::k2d),
+                b.GroupAndBinding(8, 8));
+    b.GlobalVar("texture5", b.ty.depth_multisampled_texture(ast::TextureDimension::k2d),
+                b.GroupAndBinding(9, 9));
+    b.GlobalVar("texture6", b.ty.external_texture(), b.GroupAndBinding(10, 10));
 
     b.WrapInFunction(b.Assign(b.Phony(), "buffer1"), b.Assign(b.Phony(), "buffer2"),
                      b.Assign(b.Phony(), "buffer3"), b.Assign(b.Phony(), "sampler1"),
diff --git a/src/tint/writer/float_to_string_test.cc b/src/tint/writer/float_to_string_test.cc
index 2596be7..b629b50 100644
--- a/src/tint/writer/float_to_string_test.cc
+++ b/src/tint/writer/float_to_string_test.cc
@@ -27,7 +27,7 @@
 // - 0 sign if sign is 0, 1 otherwise
 // - 'exponent_bits' is placed in the exponent space.
 //   So, the exponent bias must already be included.
-float MakeFloat(int sign, int biased_exponent, int mantissa) {
+float MakeFloat(uint32_t sign, uint32_t biased_exponent, uint32_t mantissa) {
     const uint32_t sign_bit = sign ? 0x80000000u : 0u;
     // The binary32 exponent is 8 bits, just below the sign.
     const uint32_t exponent_bits = (biased_exponent & 0xffu) << 23;
diff --git a/src/tint/writer/generate_external_texture_bindings_test.cc b/src/tint/writer/generate_external_texture_bindings_test.cc
index d0918c3..292b2ea 100644
--- a/src/tint/writer/generate_external_texture_bindings_test.cc
+++ b/src/tint/writer/generate_external_texture_bindings_test.cc
@@ -37,7 +37,7 @@
 
 TEST_F(GenerateExternalTextureBindingsTest, One) {
     ProgramBuilder b;
-    b.Global("v0", b.ty.external_texture(), b.GroupAndBinding(0, 0));
+    b.GlobalVar("v0", b.ty.external_texture(), b.GroupAndBinding(0, 0));
     b.WrapInFunction();
 
     tint::Program program(std::move(b));
@@ -54,8 +54,8 @@
 
 TEST_F(GenerateExternalTextureBindingsTest, Two_SameGroup) {
     ProgramBuilder b;
-    b.Global("v0", b.ty.external_texture(), b.GroupAndBinding(0, 0));
-    b.Global("v1", b.ty.external_texture(), b.GroupAndBinding(0, 1));
+    b.GlobalVar("v0", b.ty.external_texture(), b.GroupAndBinding(0, 0));
+    b.GlobalVar("v1", b.ty.external_texture(), b.GroupAndBinding(0, 1));
     b.WrapInFunction();
 
     tint::Program program(std::move(b));
@@ -78,8 +78,8 @@
 
 TEST_F(GenerateExternalTextureBindingsTest, Two_DifferentGroup) {
     ProgramBuilder b;
-    b.Global("v0", b.ty.external_texture(), b.GroupAndBinding(0, 0));
-    b.Global("v1", b.ty.external_texture(), b.GroupAndBinding(1, 0));
+    b.GlobalVar("v0", b.ty.external_texture(), b.GroupAndBinding(0, 0));
+    b.GlobalVar("v1", b.ty.external_texture(), b.GroupAndBinding(1, 0));
     b.WrapInFunction();
 
     tint::Program program(std::move(b));
@@ -102,11 +102,11 @@
 
 TEST_F(GenerateExternalTextureBindingsTest, Two_WithOtherBindingsInSameGroup) {
     ProgramBuilder b;
-    b.Global("v0", b.ty.i32(), b.GroupAndBinding(0, 0), kUniform);
-    b.Global("v1", b.ty.external_texture(), b.GroupAndBinding(0, 1));
-    b.Global("v2", b.ty.i32(), b.GroupAndBinding(0, 2), kUniform);
-    b.Global("v3", b.ty.external_texture(), b.GroupAndBinding(0, 3));
-    b.Global("v4", b.ty.i32(), b.GroupAndBinding(0, 4), kUniform);
+    b.GlobalVar("v0", b.ty.i32(), b.GroupAndBinding(0, 0), kUniform);
+    b.GlobalVar("v1", b.ty.external_texture(), b.GroupAndBinding(0, 1));
+    b.GlobalVar("v2", b.ty.i32(), b.GroupAndBinding(0, 2), kUniform);
+    b.GlobalVar("v3", b.ty.external_texture(), b.GroupAndBinding(0, 3));
+    b.GlobalVar("v4", b.ty.i32(), b.GroupAndBinding(0, 4), kUniform);
     b.WrapInFunction();
 
     tint::Program program(std::move(b));
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 66ff64a..fdf4557 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -58,7 +58,7 @@
 #include "src/tint/transform/fold_trivial_single_use_lets.h"
 #include "src/tint/transform/loop_to_for_loop.h"
 #include "src/tint/transform/manager.h"
-#include "src/tint/transform/promote_initializers_to_const_var.h"
+#include "src/tint/transform/promote_initializers_to_let.h"
 #include "src/tint/transform/promote_side_effects_to_decl.h"
 #include "src/tint/transform/remove_phonies.h"
 #include "src/tint/transform/renamer.h"
@@ -175,6 +175,8 @@
 
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
+        polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
@@ -226,7 +228,7 @@
         options.binding_points, options.access_controls, options.allow_collisions);
     manager.Add<transform::BindingRemapper>();
 
-    manager.Add<transform::PromoteInitializersToConstVar>();
+    manager.Add<transform::PromoteInitializersToLet>();
     manager.Add<transform::AddEmptyEntryPoint>();
     manager.Add<transform::AddSpirvBlockAttribute>();
     data.Add<transform::CanonicalizeEntryPointIO::Config>(
@@ -388,11 +390,10 @@
             return false;
         }
     }
-    out << "(";
+    ScopedParen sp(out);
     if (!EmitExpression(out, expr->expr)) {
         return false;
     }
-    out << ")";
     return true;
 }
 
@@ -432,7 +433,7 @@
         default:
             break;
     }
-    out << "(";
+    ScopedParen sp(out);
     if (!EmitExpression(out, expr->lhs)) {
         return false;
     }
@@ -440,7 +441,6 @@
     if (!EmitExpression(out, expr->rhs)) {
         return false;
     }
-    out << ")";
     return true;
 }
 
@@ -594,7 +594,7 @@
         return EmitFloatModulo(out, expr);
     }
 
-    out << "(";
+    ScopedParen sp(out);
     if (!EmitExpression(out, expr->lhs)) {
         return false;
     }
@@ -670,7 +670,6 @@
         return false;
     }
 
-    out << ")";
     return true;
 }
 
@@ -730,7 +729,8 @@
     auto name = builder_.Symbols().NameFor(ident->symbol);
     auto caller_sym = ident->symbol;
 
-    out << name << "(";
+    out << name;
+    ScopedParen sp(out);
 
     bool first = true;
     for (auto* arg : args) {
@@ -744,7 +744,6 @@
         }
     }
 
-    out << ")";
     return true;
 }
 
@@ -809,7 +808,8 @@
         return false;
     }
 
-    out << name << "(";
+    out << name;
+    ScopedParen sp(out);
 
     bool first = true;
     for (auto* arg : call->Arguments()) {
@@ -823,7 +823,6 @@
         }
     }
 
-    out << ")";
     return true;
 }
 
@@ -833,13 +832,12 @@
     if (!EmitType(out, conv->Target(), ast::StorageClass::kNone, ast::Access::kReadWrite, "")) {
         return false;
     }
-    out << "(";
+    ScopedParen sp(out);
 
     if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
         return false;
     }
 
-    out << ")";
     return true;
 }
 
@@ -1183,7 +1181,9 @@
         }
     }
 
-    out << fn << "(";
+    out << fn;
+    ScopedParen sp(out);
+
     if (!EmitExpression(out, expr->args[0])) {
         return false;
     }
@@ -1191,7 +1191,6 @@
     if (!EmitExpression(out, expr->args[1])) {
         return false;
     }
-    out << ")";
     return true;
 }
 
@@ -1333,7 +1332,7 @@
 const ast::Expression* GeneratorImpl::CreateF32Zero(const sem::Statement* stmt) {
     auto* zero = builder_.Expr(0_f);
     auto* f32 = builder_.create<sem::F32>();
-    auto* sem_zero = builder_.create<sem::Expression>(zero, f32, stmt, sem::Constant{},
+    auto* sem_zero = builder_.create<sem::Expression>(zero, f32, stmt, /* constant_value */ nullptr,
                                                       /* has_side_effects */ false);
     builder_.Sem().Add(zero, sem_zero);
     return zero;
@@ -1350,8 +1349,8 @@
 
     // Returns the argument with the given usage
     auto arg = [&](Usage usage) {
-        int idx = signature.IndexOf(usage);
-        return (idx >= 0) ? arguments[idx] : nullptr;
+        auto idx = signature.IndexOf(usage);
+        return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
     };
 
     auto* texture = arg(Usage::kTexture);
@@ -1608,10 +1607,13 @@
     switch (builtin->Type()) {
         case sem::BuiltinType::kAbs:
         case sem::BuiltinType::kAcos:
+        case sem::BuiltinType::kAcosh:
         case sem::BuiltinType::kAll:
         case sem::BuiltinType::kAny:
         case sem::BuiltinType::kAsin:
+        case sem::BuiltinType::kAsinh:
         case sem::BuiltinType::kAtan:
+        case sem::BuiltinType::kAtanh:
         case sem::BuiltinType::kCeil:
         case sem::BuiltinType::kClamp:
         case sem::BuiltinType::kCos:
@@ -1769,7 +1771,7 @@
 
 bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) {
     if (auto* sem = builder_.Sem().Get(expr)) {
-        if (auto constant = sem->ConstantValue()) {
+        if (auto* constant = sem->ConstantValue()) {
             return EmitConstant(out, constant);
         }
     }
@@ -1930,6 +1932,9 @@
         },
         [&](const ast::Let* let) { return EmitProgramConstVariable(let); },
         [&](const ast::Override* override) { return EmitOverride(override); },
+        [&](const ast::Const*) {
+            return true;  // Constants are embedded at their use
+        },
         [&](Default) {
             TINT_ICE(Writer, diagnostics_)
                 << "unhandled global variable type " << global->TypeInfo().name;
@@ -2133,7 +2138,7 @@
         // Emit the layout(local_size) attributes.
         auto wgsize = func_sem->WorkgroupSize();
         out << "layout(";
-        for (int i = 0; i < 3; i++) {
+        for (size_t i = 0; i < 3; i++) {
             if (i > 0) {
                 out << ", ";
             }
@@ -2209,90 +2214,85 @@
     return true;
 }
 
-bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant& constant) {
-    auto emit_bool = [&](size_t element_idx) {
-        out << (constant.Element<AInt>(element_idx) ? "true" : "false");
-        return true;
-    };
-    auto emit_f32 = [&](size_t element_idx) {
-        PrintF32(out, static_cast<float>(constant.Element<AFloat>(element_idx)));
-        return true;
-    };
-    auto emit_i32 = [&](size_t element_idx) {
-        out << constant.Element<AInt>(element_idx).value;
-        return true;
-    };
-    auto emit_u32 = [&](size_t element_idx) {
-        out << constant.Element<AInt>(element_idx).value << "u";
-        return true;
-    };
-    auto emit_vector = [&](const sem::Vector* vec_ty, size_t start, size_t end) {
-        if (!EmitType(out, vec_ty, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
-            return false;
-        }
-
-        ScopedParen sp(out);
-
-        auto emit_els = [&](auto emit_el) {
-            if (constant.AllEqual(start, end)) {
-                return emit_el(start);
+bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) {
+    return Switch(
+        constant->Type(),  //
+        [&](const sem::Bool*) {
+            out << (constant->As<AInt>() ? "true" : "false");
+            return true;
+        },
+        [&](const sem::F32*) {
+            PrintF32(out, constant->As<float>());
+            return true;
+        },
+        [&](const sem::I32*) {
+            out << constant->As<AInt>();
+            return true;
+        },
+        [&](const sem::U32*) {
+            out << constant->As<AInt>() << "u";
+            return true;
+        },
+        [&](const sem::Vector* v) {
+            if (!EmitType(out, v, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                return false;
             }
-            for (size_t i = start; i < end; i++) {
-                if (i > start) {
+
+            ScopedParen sp(out);
+
+            if (constant->AllEqual()) {
+                return EmitConstant(out, constant->Index(0));
+            }
+
+            for (size_t i = 0; i < v->Width(); i++) {
+                if (i > 0) {
                     out << ", ";
                 }
-                if (!emit_el(i)) {
+                if (!EmitConstant(out, constant->Index(i))) {
                     return false;
                 }
             }
             return true;
-        };
-
-        return Switch(
-            vec_ty->type(),                                         //
-            [&](const sem::Bool*) { return emit_els(emit_bool); },  //
-            [&](const sem::F32*) { return emit_els(emit_f32); },    //
-            [&](const sem::I32*) { return emit_els(emit_i32); },    //
-            [&](const sem::U32*) { return emit_els(emit_u32); },    //
-            [&](Default) {
-                diagnostics_.add_error(diag::System::Writer,
-                                       "unhandled constant vector element type: " +
-                                           builder_.FriendlyName(vec_ty->type()));
-                return false;
-            });
-    };
-    auto emit_matrix = [&](const sem::Matrix* m) {
-        if (!EmitType(out, constant.Type(), ast::StorageClass::kNone, ast::Access::kUndefined,
-                      "")) {
-            return false;
-        }
-
-        ScopedParen sp(out);
-
-        for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) {
-            if (column_idx > 0) {
-                out << ", ";
-            }
-            size_t start = m->rows() * column_idx;
-            size_t end = m->rows() * (column_idx + 1);
-            if (!emit_vector(m->ColumnType(), start, end)) {
+        },
+        [&](const sem::Matrix* m) {
+            if (!EmitType(out, m, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
-        }
-        return true;
-    };
-    return Switch(
-        constant.Type(),                                                                   //
-        [&](const sem::Bool*) { return emit_bool(0); },                                    //
-        [&](const sem::F32*) { return emit_f32(0); },                                      //
-        [&](const sem::I32*) { return emit_i32(0); },                                      //
-        [&](const sem::U32*) { return emit_u32(0); },                                      //
-        [&](const sem::Vector* v) { return emit_vector(v, 0, constant.ElementCount()); },  //
-        [&](const sem::Matrix* m) { return emit_matrix(m); },                              //
+
+            ScopedParen sp(out);
+
+            for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) {
+                if (column_idx > 0) {
+                    out << ", ";
+                }
+                if (!EmitConstant(out, constant->Index(column_idx))) {
+                    return false;
+                }
+            }
+            return true;
+        },
+        [&](const sem::Array* a) {
+            if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                return false;
+            }
+
+            ScopedParen sp(out);
+
+            for (size_t i = 0; i < a->Count(); i++) {
+                if (i > 0) {
+                    out << ", ";
+                }
+                if (!EmitConstant(out, constant->Index(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
         [&](Default) {
             diagnostics_.add_error(
                 diag::System::Writer,
-                "unhandled constant type: " + builder_.FriendlyName(constant.Type()));
+                "unhandled constant type: " + builder_.FriendlyName(constant->Type()));
             return false;
         });
 }
@@ -2361,7 +2361,7 @@
             return false;
         }
         bool first = true;
-        out << "(";
+        ScopedParen sp(out);
         for (auto* member : str->Members()) {
             if (!first) {
                 out << ", ";
@@ -2370,19 +2370,17 @@
             }
             EmitZeroValue(out, member->Type());
         }
-        out << ")";
     } else if (auto* array = type->As<sem::Array>()) {
         if (!EmitType(out, type, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
             return false;
         }
-        out << "(";
+        ScopedParen sp(out);
         for (uint32_t i = 0; i < array->Count(); i++) {
             if (i != 0) {
                 out << ", ";
             }
             EmitZeroValue(out, array->ElemType());
         }
-        out << ")";
     } else {
         diagnostics_.add_error(diag::System::Writer, "Invalid type for zero emission: " +
                                                          type->FriendlyName(builder_.Symbols()));
@@ -2659,6 +2657,9 @@
             v->variable,  //
             [&](const ast::Var* var) { return EmitVar(var); },
             [&](const ast::Let* let) { return EmitLet(let); },
+            [&](const ast::Const*) {
+                return true;  // Constants are embedded at their use
+            },
             [&](Default) {  //
                 TINT_ICE(Writer, diagnostics_)
                     << "unknown variable type: " << v->variable->TypeInfo().name;
@@ -2934,14 +2935,12 @@
             out << "-";
             break;
     }
-    out << "(";
 
+    ScopedParen sp(out);
     if (!EmitExpression(out, expr->expr)) {
         return false;
     }
 
-    out << ")";
-
     return true;
 }
 
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index 5b07fd3..15b7ee1 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -346,7 +346,7 @@
     /// @param out the output stream
     /// @param constant the constant value to emit
     /// @returns true if the constant value was successfully emitted
-    bool EmitConstant(std::ostream& out, const sem::Constant& constant);
+    bool EmitConstant(std::ostream& out, const sem::Constant* constant);
     /// Handles a literal
     /// @param out the output stream
     /// @param lit the literal to emit
diff --git a/src/tint/writer/glsl/generator_impl_array_accessor_test.cc b/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
index d28e560..bbf90fe 100644
--- a/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_array_accessor_test.cc
@@ -22,7 +22,7 @@
 using GlslGeneratorImplTest_Expression = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Expression, IndexAccessor) {
-    Global("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
     auto* expr = IndexAccessor("ary", 5_i);
     WrapInFunction(expr);
 
diff --git a/src/tint/writer/glsl/generator_impl_assign_test.cc b/src/tint/writer/glsl/generator_impl_assign_test.cc
index fbd9f62..84b6b30 100644
--- a/src/tint/writer/glsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/glsl/generator_impl_assign_test.cc
@@ -20,8 +20,8 @@
 using GlslGeneratorImplTest_Assign = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Assign, Emit_Assign) {
-    Global("lhs", ty.i32(), ast::StorageClass::kPrivate);
-    Global("rhs", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.i32(), ast::StorageClass::kPrivate);
     auto* assign = Assign("lhs", "rhs");
     WrapInFunction(assign);
 
diff --git a/src/tint/writer/glsl/generator_impl_binary_test.cc b/src/tint/writer/glsl/generator_impl_binary_test.cc
index 0d019a9..b529255 100644
--- a/src/tint/writer/glsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/glsl/generator_impl_binary_test.cc
@@ -43,8 +43,8 @@
         return;
     }
 
-    Global("left", ty.f32(), ast::StorageClass::kPrivate);
-    Global("right", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("right", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -62,8 +62,8 @@
 TEST_P(GlslBinaryTest, Emit_u32) {
     auto params = GetParam();
 
-    Global("left", ty.u32(), ast::StorageClass::kPrivate);
-    Global("right", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("right", ty.u32(), ast::StorageClass::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -86,8 +86,8 @@
         return;
     }
 
-    Global("left", ty.i32(), ast::StorageClass::kPrivate);
-    Global("right", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("right", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -153,7 +153,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixScalar) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_f);
 
@@ -168,7 +168,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_ScalarMatrix) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("mat");
 
@@ -183,7 +183,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixVector) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = vec3<f32>(1_f, 1_f, 1_f);
 
@@ -198,7 +198,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_VectorMatrix) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = vec3<f32>(1_f, 1_f, 1_f);
     auto* rhs = Expr("mat");
 
@@ -213,8 +213,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Multiply_MatrixMatrix) {
-    Global("lhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
-    Global("rhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -227,8 +227,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Logical_And) {
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -246,8 +246,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModF32) {
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
-    Global("b", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -260,8 +260,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, ModVec3F32) {
-    Global("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kModulo, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -275,10 +275,10 @@
 
 TEST_F(GlslGeneratorImplTest_Binary, Logical_Multi) {
     // (a && b) || (c || d)
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -307,8 +307,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Binary, Logical_Or) {
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -334,9 +334,9 @@
     //   return 3i;
     // }
 
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr =
         If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
@@ -371,9 +371,9 @@
 TEST_F(GlslGeneratorImplTest_Binary, Return_WithLogical) {
     // return (a && b) || c;
 
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = Return(create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -399,10 +399,10 @@
 TEST_F(GlslGeneratorImplTest_Binary, Assign_WithLogical) {
     // a = (b || c) && d;
 
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr =
         Assign(Expr("a"),
@@ -430,9 +430,9 @@
 TEST_F(GlslGeneratorImplTest_Binary, Decl_WithLogical) {
     // var a : bool = (b && c) || d;
 
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* var =
         Var("a", ty.bool_(), ast::StorageClass::kNone,
@@ -469,10 +469,10 @@
              Param(Sym(), ty.bool_()),
          },
          ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     ast::ExpressionList params;
     params.push_back(
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index f7572b2..c753293 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -153,13 +153,13 @@
 TEST_P(GlslBuiltinTest, Emit) {
     auto param = GetParam();
 
-    Global("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    Global("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
-    Global("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    Global("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    Global("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
-    Global("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = GenerateCall(param.builtin, param.type, this);
     ASSERT_NE(nullptr, call) << "Unhandled builtin";
@@ -242,8 +242,8 @@
 TEST_F(GlslGeneratorImplTest_Builtin, Builtin_Call) {
     auto* call = Call("dot", "param1", "param2");
 
-    Global("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     WrapInFunction(CallStmt(call));
 
@@ -584,7 +584,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack4x8Snorm) {
     auto* call = Call("pack4x8snorm", "p1");
-    Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -602,7 +602,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack4x8Unorm) {
     auto* call = Call("pack4x8unorm", "p1");
-    Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -620,7 +620,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack2x16Snorm) {
     auto* call = Call("pack2x16snorm", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -638,7 +638,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack2x16Unorm) {
     auto* call = Call("pack2x16unorm", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -656,7 +656,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Pack2x16Float) {
     auto* call = Call("pack2x16float", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -674,7 +674,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack4x8Snorm) {
     auto* call = Call("unpack4x8snorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -692,7 +692,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack4x8Unorm) {
     auto* call = Call("unpack4x8unorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -710,7 +710,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack2x16Snorm) {
     auto* call = Call("unpack2x16snorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -728,7 +728,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack2x16Unorm) {
     auto* call = Call("unpack2x16unorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -746,7 +746,7 @@
 
 TEST_F(GlslGeneratorImplTest_Builtin, Unpack2x16Float) {
     auto* call = Call("unpack2x16float", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -803,7 +803,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, DotI32) {
-    Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(Call("dot", "v", "v")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
@@ -831,9 +831,9 @@
 TEST_F(GlslGeneratorImplTest_Builtin, FMA) {
     auto* call = Call("fma", "a", "b", "c");
 
-    Global("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("c", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     WrapInFunction(CallStmt(call));
 
@@ -846,7 +846,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_Builtin, DotU32) {
-    Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(Call("dot", "v", "v")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/glsl/generator_impl_call_test.cc b/src/tint/writer/glsl/generator_impl_call_test.cc
index c8a1de2..d1866ea 100644
--- a/src/tint/writer/glsl/generator_impl_call_test.cc
+++ b/src/tint/writer/glsl/generator_impl_call_test.cc
@@ -42,8 +42,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.f32(), {Return(1.23_f)});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -62,8 +62,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = CallStmt(Call("my_func", "param1", "param2"));
     WrapInFunction(call);
diff --git a/src/tint/writer/glsl/generator_impl_constructor_test.cc b/src/tint/writer/glsl/generator_impl_constructor_test.cc
index e70ecaf..9a937e8 100644
--- a/src/tint/writer/glsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_constructor_test.cc
@@ -189,8 +189,7 @@
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr("vec3[3](vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f),"
-                                        " vec3(0.0f, 0.0f, 0.0f))"));
+    EXPECT_THAT(gen.result(), HasSubstr("vec3[3](vec3(0.0f), vec3(0.0f), vec3(0.0f))"));
 }
 
 TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Struct) {
diff --git a/src/tint/writer/glsl/generator_impl_function_test.cc b/src/tint/writer/glsl/generator_impl_function_test.cc
index 13e32fc..a70e238 100644
--- a/src/tint/writer/glsl/generator_impl_function_test.cc
+++ b/src/tint/writer/glsl/generator_impl_function_test.cc
@@ -349,11 +349,11 @@
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
     auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())});
-    auto* ubo = Global("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(1),
-                       });
+    auto* ubo = GlobalVar("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(1u),
+                          });
 
     Func("sub_func",
          {
@@ -403,11 +403,11 @@
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_UniformStruct) {
     auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())});
 
-    Global("uniforms", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("uniforms", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone,
                     MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
@@ -448,11 +448,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
@@ -498,11 +498,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
@@ -549,11 +549,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("frag_main", {}, ty.void_(),
          {
@@ -597,11 +597,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("frag_main", {}, ty.void_(),
          {
@@ -641,11 +641,11 @@
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
     auto* s = Structure("S", {Member("x", ty.f32())});
-    Global("coord", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("sub_func", {Param("param", ty.f32())}, ty.f32(),
          {
@@ -690,11 +690,11 @@
 
 TEST_F(GlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
     auto* s = Structure("S", {Member("x", ty.f32())});
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("sub_func", {Param("param", ty.f32())}, ty.f32(),
          {
@@ -820,9 +820,6 @@
     ASSERT_TRUE(gen.Generate()) << gen.error();
     EXPECT_EQ(gen.result(), R"(#version 310 es
 
-const int width = 2;
-const int height = 3;
-const int depth = 4;
 layout(local_size_x = 2, local_size_y = 3, local_size_z = 4) in;
 void main() {
   return;
@@ -922,11 +919,11 @@
 
     auto* s = Structure("Data", {Member("d", ty.f32())});
 
-    Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
diff --git a/src/tint/writer/glsl/generator_impl_identifier_test.cc b/src/tint/writer/glsl/generator_impl_identifier_test.cc
index 396c261..ff9bc88 100644
--- a/src/tint/writer/glsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/glsl/generator_impl_identifier_test.cc
@@ -20,7 +20,7 @@
 using GlslGeneratorImplTest_Identifier = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Identifier, EmitIdentifierExpression) {
-    Global("foo", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("foo", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* i = Expr("foo");
     WrapInFunction(i);
diff --git a/src/tint/writer/glsl/generator_impl_if_test.cc b/src/tint/writer/glsl/generator_impl_if_test.cc
index 4b0b7bb..82dc307 100644
--- a/src/tint/writer/glsl/generator_impl_if_test.cc
+++ b/src/tint/writer/glsl/generator_impl_if_test.cc
@@ -20,7 +20,7 @@
 using GlslGeneratorImplTest_If = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_If, Emit_If) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* cond = Expr("cond");
     auto* body = Block(Return());
@@ -38,8 +38,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_If, Emit_IfWithElseIf) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_cond = Expr("else_cond");
     auto* else_body = Block(Return());
@@ -65,7 +65,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_If, Emit_IfWithElse) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_body = Block(Return());
 
@@ -88,8 +88,8 @@
 }
 
 TEST_F(GlslGeneratorImplTest_If, Emit_IfWithMultiple) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_cond = Expr("else_cond");
 
diff --git a/src/tint/writer/glsl/generator_impl_import_test.cc b/src/tint/writer/glsl/generator_impl_import_test.cc
index 2843f98..9e988f1 100644
--- a/src/tint/writer/glsl/generator_impl_import_test.cc
+++ b/src/tint/writer/glsl/generator_impl_import_test.cc
@@ -256,7 +256,7 @@
                          testing::Values(GlslImportData{"clamp", "clamp"}));
 
 TEST_F(GlslGeneratorImplTest_Import, GlslImportData_Determinant) {
-    Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("determinant", "var");
     WrapInFunction(expr);
diff --git a/src/tint/writer/glsl/generator_impl_loop_test.cc b/src/tint/writer/glsl/generator_impl_loop_test.cc
index ec9219a..9da50f0 100644
--- a/src/tint/writer/glsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/glsl/generator_impl_loop_test.cc
@@ -66,8 +66,8 @@
 TEST_F(GlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    Global("lhs", ty.f32(), ast::StorageClass::kPrivate);
-    Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* body = Block(create<ast::DiscardStatement>());
     auto* continuing = Block(CallStmt(Call("a_statement")));
@@ -112,7 +112,7 @@
     //   }
     // }
 
-    Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.4_f))),  //
                        Decl(Var("other", ty.f32())),             //
diff --git a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
index fcfa9df..875d211 100644
--- a/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_member_accessor_test.cc
@@ -91,11 +91,11 @@
 
         auto* s = b.Structure("Data", members);
 
-        b.Global("data", b.ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                 ast::AttributeList{
-                     b.create<ast::BindingAttribute>(0),
-                     b.create<ast::GroupAttribute>(1),
-                 });
+        b.GlobalVar("data", b.ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                    ast::AttributeList{
+                        b.create<ast::BindingAttribute>(0u),
+                        b.create<ast::GroupAttribute>(1u),
+                    });
     }
 
     void SetupFunction(ast::StatementList statements) {
@@ -115,7 +115,7 @@
 
 TEST_F(GlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
     auto* s = Structure("Data", {Member("mem", ty.f32())});
-    Global("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
 
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(Var("expr", ty.f32(), ast::StorageClass::kNone, expr));
diff --git a/src/tint/writer/glsl/generator_impl_module_constant_test.cc b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
index a97d4cd..b483511 100644
--- a/src/tint/writer/glsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
@@ -22,7 +22,7 @@
 
 using GlslGeneratorImplTest_ModuleConstant = TestHelper;
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_ModuleConstant) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalLet) {
     auto* var = Let("pos", ty.array<f32, 3>(), array<f32, 3>(1_f, 2_f, 3_f));
     WrapInFunction(Decl(var));
 
@@ -32,7 +32,216 @@
     EXPECT_EQ(gen.result(), "const float pos[3] = float[3](1.0f, 2.0f, 3.0f);\n");
 }
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AInt) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_i32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_u32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  uint l = 1u;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_f32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  ivec3 l = ivec3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_f32) {
+    auto* var = GlobalConst("G", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_f32) {
+    auto* var = GlobalConst("G", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l[3] = float[3](1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_vec2_bool) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                                      vec2<bool>(true, false),         //
+                                      vec2<bool>(false, true),         //
+                                      vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  bvec2 l[3] = bvec2[3](bvec2(true, false), bvec2(false, true), bvec2(true));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_Override) {
     auto* var = Override("pos", ty.f32(), Expr(3_f),
                          ast::AttributeList{
                              Id(23),
@@ -48,7 +257,7 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoConstructor) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_Override_NoConstructor) {
     auto* var = Override("pos", ty.f32(), nullptr,
                          ast::AttributeList{
                              Id(23),
@@ -64,7 +273,7 @@
 )");
 }
 
-TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoId) {
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_Override_NoId) {
     auto* a = Override("a", ty.f32(), Expr(3_f),
                        ast::AttributeList{
                            Id(0),
diff --git a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
index f2b57c7..ae1b0ba 100644
--- a/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_sanitizer_test.cc
@@ -26,11 +26,11 @@
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -69,11 +69,11 @@
                                          Member(0, "z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -111,11 +111,11 @@
 
 TEST_F(GlslSanitizerTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
@@ -156,12 +156,11 @@
 
 TEST_F(GlslSanitizerTest, PromoteArrayInitializerToConstVar) {
     auto* array_init = array<i32, 4>(1_i, 2_i, 3_i, 4_i);
-    auto* array_index = IndexAccessor(array_init, 3_i);
-    auto* pos = Var("pos", ty.i32(), ast::StorageClass::kNone, array_index);
 
     Func("main", {}, ty.void_(),
          {
-             Decl(pos),
+             Decl(Var("idx", nullptr, Expr(3_i))),
+             Decl(Var("pos", ty.i32(), IndexAccessor(array_init, "idx"))),
          },
          {
              Stage(ast::PipelineStage::kFragment),
@@ -176,8 +175,9 @@
 precision mediump float;
 
 void tint_symbol() {
+  int idx = 3;
   int tint_symbol_1[4] = int[4](1, 2, 3, 4);
-  int pos = tint_symbol_1[3];
+  int pos = tint_symbol_1[idx];
 }
 
 void main() {
diff --git a/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc b/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
index a12fbbc..7a13b66 100644
--- a/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_storage_buffer_test.cc
@@ -35,11 +35,11 @@
                                       ctx->Member("dewey", ctx->ty.f32(), {ctx->MemberAlign(256)}),
                                       ctx->Member("louie", ctx->ty.f32(), {ctx->MemberAlign(256)}),
                                   });
-    ctx->Global("nephews", ctx->ty.Of(nephews), ast::StorageClass::kStorage,
-                ast::AttributeList{
-                    ctx->create<ast::BindingAttribute>(0),
-                    ctx->create<ast::GroupAttribute>(0),
-                });
+    ctx->GlobalVar("nephews", ctx->ty.Of(nephews), ast::StorageClass::kStorage,
+                   ast::AttributeList{
+                       ctx->create<ast::BindingAttribute>(0u),
+                       ctx->create<ast::GroupAttribute>(0u),
+                   });
 }
 
 TEST_F(GlslGeneratorImplTest_StorageBuffer, Align) {
diff --git a/src/tint/writer/glsl/generator_impl_switch_test.cc b/src/tint/writer/glsl/generator_impl_switch_test.cc
index 1cc42fb..c8957be 100644
--- a/src/tint/writer/glsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/glsl/generator_impl_switch_test.cc
@@ -22,7 +22,7 @@
 using GlslGeneratorImplTest_Switch = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch) {
-    Global("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* def_body = Block(create<ast::BreakStatement>());
     auto* def = create<ast::CaseStatement>(ast::CaseSelectorList{}, def_body);
diff --git a/src/tint/writer/glsl/generator_impl_test.cc b/src/tint/writer/glsl/generator_impl_test.cc
index 70e5c0b..3dbf601 100644
--- a/src/tint/writer/glsl/generator_impl_test.cc
+++ b/src/tint/writer/glsl/generator_impl_test.cc
@@ -57,12 +57,12 @@
 }
 
 TEST_F(GlslGeneratorImplTest, GenerateSampleIndexES) {
-    Global("gl_SampleID", ty.i32(),
-           ast::AttributeList{
-               Builtin(ast::Builtin::kSampleIndex),
-               Disable(ast::DisabledValidation::kIgnoreStorageClass),
-           },
-           ast::StorageClass::kInput);
+    GlobalVar("gl_SampleID", ty.i32(),
+              ast::AttributeList{
+                  Builtin(ast::Builtin::kSampleIndex),
+                  Disable(ast::DisabledValidation::kIgnoreStorageClass),
+              },
+              ast::StorageClass::kInput);
     Func("my_func", {}, ty.i32(),
          {
              Return(Expr("gl_SampleID")),
@@ -82,12 +82,12 @@
 }
 
 TEST_F(GlslGeneratorImplTest, GenerateSampleIndexDesktop) {
-    Global("gl_SampleID", ty.i32(),
-           ast::AttributeList{
-               Builtin(ast::Builtin::kSampleIndex),
-               Disable(ast::DisabledValidation::kIgnoreStorageClass),
-           },
-           ast::StorageClass::kInput);
+    GlobalVar("gl_SampleID", ty.i32(),
+              ast::AttributeList{
+                  Builtin(ast::Builtin::kSampleIndex),
+                  Disable(ast::DisabledValidation::kIgnoreStorageClass),
+              },
+              ast::StorageClass::kInput);
     Func("my_func", {}, ty.i32(),
          {
              Return(Expr("gl_SampleID")),
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index b7daee2..acf28ff 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -33,7 +33,7 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_Array) {
     auto* arr = ty.array<bool, 4>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -46,7 +46,7 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
     auto* arr = ty.array(ty.array<bool, 4>(), 5_u);
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -59,7 +59,7 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) {
     auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5_u), 6_u);
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -72,7 +72,7 @@
 
 TEST_F(GlslGeneratorImplTest_Type, EmitType_Array_WithoutName) {
     auto* arr = ty.array<bool, 4>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -134,7 +134,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -154,7 +154,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -170,7 +170,7 @@
                                  Member("double", ty.i32()),
                                  Member("float", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -187,7 +187,7 @@
                                  Member("a", ty.i32(), {MemberOffset(0)}),
                                  Member("b", ty.f32(), {MemberOffset(8)}),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -270,11 +270,11 @@
 
     auto* t = ty.depth_texture(params.dim);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
@@ -297,11 +297,11 @@
 TEST_F(GlslDepthMultisampledTexturesTest, Emit) {
     auto* t = ty.depth_multisampled_texture(ast::TextureDimension::k2d);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
@@ -340,11 +340,11 @@
     }
     auto* t = ty.sampled_texture(params.dim, datatype);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
@@ -474,11 +474,11 @@
 
     auto* t = ty.storage_texture(params.dim, params.imgfmt, ast::Access::kWrite);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
diff --git a/src/tint/writer/glsl/generator_impl_unary_op_test.cc b/src/tint/writer/glsl/generator_impl_unary_op_test.cc
index 0e318fc..22012c8 100644
--- a/src/tint/writer/glsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/glsl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using GlslUnaryOpTest = TestHelper;
 
 TEST_F(GlslUnaryOpTest, AddressOf) {
-    Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Complement) {
-    Global("expr", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.u32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Indirection) {
-    Global("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
     auto* p =
         Let("expr", nullptr, create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
@@ -58,7 +58,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Not) {
-    Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -70,7 +70,7 @@
 }
 
 TEST_F(GlslUnaryOpTest, Negation) {
-    Global("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc b/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
index 8709b3d..8e653b5 100644
--- a/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
+++ b/src/tint/writer/glsl/generator_impl_uniform_buffer_test.cc
@@ -24,7 +24,7 @@
 
 TEST_F(GlslGeneratorImplTest_UniformBuffer, Simple) {
     auto* simple = Structure("Simple", {Member("member", ty.f32())});
-    Global("simple", ty.Of(simple), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+    GlobalVar("simple", ty.Of(simple), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
 
     GeneratorImpl& gen = Build();
 
@@ -44,7 +44,7 @@
 
 TEST_F(GlslGeneratorImplTest_UniformBuffer, Simple_Desktop) {
     auto* simple = Structure("Simple", {Member("member", ty.f32())});
-    Global("simple", ty.Of(simple), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+    GlobalVar("simple", ty.Of(simple), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
 
     GeneratorImpl& gen = Build(Version(Version::Standard::kDesktop, 4, 4));
 
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index 5d95bc6..daa664a 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -16,6 +16,8 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/glsl/test_helper.h"
 
+using namespace tint::number_suffixes;  // NOLINT
+
 namespace tint::writer::glsl {
 namespace {
 
@@ -36,7 +38,7 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Let) {
     auto* var = Let("a", ty.f32(), Construct(ty.f32()));
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -49,6 +51,228 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+    auto* var = Const("a", ty.f32(), Construct(ty.f32()));
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  int l = 1;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  uint l = 1u;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  ivec3 l = ivec3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  vec3 l = vec3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  mat2x3 l = mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  float l[3] = float[3](1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#version 310 es
+
+void f() {
+  bvec2 l[3] = bvec2[3](bvec2(true, false), bvec2(false, true), bvec2(true));
+}
+
+)");
+}
+
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
     auto* var = Var("a", ty.array<f32, 5>());
 
@@ -64,7 +288,7 @@
 }
 
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Private) {
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     WrapInFunction(Expr("a"));
 
@@ -76,19 +300,6 @@
     EXPECT_THAT(gen.result(), HasSubstr("  float a = 0.0f;\n"));
 }
 
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_Private) {
-    Global("initializer", ty.f32(), ast::StorageClass::kPrivate);
-    Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
-
-    WrapInFunction(Expr("a"));
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr(R"(float a = initializer;
-)"));
-}
-
 TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec) {
     auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
 
diff --git a/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc b/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
index faa675d..1d96441 100644
--- a/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
+++ b/src/tint/writer/glsl/generator_impl_workgroup_var_test.cc
@@ -27,7 +27,7 @@
 using GlslGeneratorImplTest_WorkgroupVar = TestHelper;
 
 TEST_F(GlslGeneratorImplTest_WorkgroupVar, Basic) {
-    Global("wg", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.f32(), ast::StorageClass::kWorkgroup);
 
     Func("main", {}, ty.void_(), {Assign("wg", 1.2_f)},
          {
@@ -43,7 +43,7 @@
 TEST_F(GlslGeneratorImplTest_WorkgroupVar, Aliased) {
     auto* alias = Alias("F32", ty.f32());
 
-    Global("wg", ty.Of(alias), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.Of(alias), ast::StorageClass::kWorkgroup);
 
     Func("main", {}, ty.void_(), {Assign("wg", 1.2_f)},
          {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 3dfb74f..b307c8d 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -60,7 +60,7 @@
 #include "src/tint/transform/loop_to_for_loop.h"
 #include "src/tint/transform/manager.h"
 #include "src/tint/transform/num_workgroups_from_uniform.h"
-#include "src/tint/transform/promote_initializers_to_const_var.h"
+#include "src/tint/transform/promote_initializers_to_let.h"
 #include "src/tint/transform/promote_side_effects_to_decl.h"
 #include "src/tint/transform/remove_continue_in_switch.h"
 #include "src/tint/transform/remove_phonies.h"
@@ -158,6 +158,9 @@
 
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
+        polyfills.acosh = transform::BuiltinPolyfill::Level::kFull;
+        polyfills.asinh = true;
+        polyfills.atanh = transform::BuiltinPolyfill::Level::kFull;
         // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
         // and `firstbithigh`.
         polyfills.count_leading_zeros = true;
@@ -231,7 +234,7 @@
     // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
     // will be transformed by CalculateArrayLength
     manager.Add<transform::CalculateArrayLength>();
-    manager.Add<transform::PromoteInitializersToConstVar>();
+    manager.Add<transform::PromoteInitializersToLet>();
 
     manager.Add<transform::RemoveContinueInSwitch>();
 
@@ -612,8 +615,7 @@
             if (auto* mat = TypeOf(lhs_sub_access->object)->UnwrapRef()->As<sem::Matrix>()) {
                 auto* rhs_col_idx_sem = builder_.Sem().Get(lhs_access->index);
                 auto* rhs_row_idx_sem = builder_.Sem().Get(lhs_sub_access->index);
-                if (!rhs_col_idx_sem->ConstantValue().IsValid() ||
-                    !rhs_row_idx_sem->ConstantValue().IsValid()) {
+                if (!rhs_col_idx_sem->ConstantValue() || !rhs_row_idx_sem->ConstantValue()) {
                     return EmitDynamicMatrixScalarAssignment(stmt, mat);
                 }
             }
@@ -623,7 +625,7 @@
         const auto* lhs_access_type = TypeOf(lhs_access->object)->UnwrapRef();
         if (auto* mat = lhs_access_type->As<sem::Matrix>()) {
             auto* lhs_index_sem = builder_.Sem().Get(lhs_access->index);
-            if (!lhs_index_sem->ConstantValue().IsValid()) {
+            if (!lhs_index_sem->ConstantValue()) {
                 return EmitDynamicMatrixVectorAssignment(stmt, mat);
             }
         }
@@ -631,7 +633,7 @@
         // indices
         if (auto* vec = lhs_access_type->As<sem::Vector>()) {
             auto* rhs_sem = builder_.Sem().Get(lhs_access->index);
-            if (!rhs_sem->ConstantValue().IsValid()) {
+            if (!rhs_sem->ConstantValue()) {
                 return EmitDynamicVectorAssignment(stmt, vec);
             }
         }
@@ -651,28 +653,30 @@
 
 bool GeneratorImpl::EmitExpressionOrOneIfZero(std::ostream& out, const ast::Expression* expr) {
     // For constants, replace literal 0 with 1.
-    if (const auto& val = builder_.Sem().Get(expr)->ConstantValue()) {
-        if (!val.AnyZero()) {
+    if (const auto* val = builder_.Sem().Get(expr)->ConstantValue()) {
+        if (!val->AnyZero()) {
             return EmitExpression(out, expr);
         }
 
-        if (val.Type()->IsAnyOf<sem::I32, sem::U32>()) {
-            return EmitValue(out, val.Type(), 1);
+        auto* ty = val->Type();
+
+        if (ty->IsAnyOf<sem::I32, sem::U32>()) {
+            return EmitValue(out, ty, 1);
         }
 
-        if (auto* vec = val.Type()->As<sem::Vector>()) {
+        if (auto* vec = ty->As<sem::Vector>()) {
             auto* elem_ty = vec->type();
 
-            if (!EmitType(out, val.Type(), ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+            if (!EmitType(out, ty, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
 
             out << "(";
-            for (size_t i = 0; i < val.ElementCount(); ++i) {
+            for (size_t i = 0; i < vec->Width(); ++i) {
                 if (i != 0) {
                     out << ", ";
                 }
-                auto s = val.Element<AInt>(i).value;
+                auto s = val->Index(i)->As<AInt>();
                 if (!EmitValue(out, elem_ty, (s == 0) ? 1 : static_cast<int>(s))) {
                     return false;
                 }
@@ -1110,52 +1114,13 @@
         return EmitZeroValue(out, type);
     }
 
-    if (auto* mat = call->Type()->As<sem::Matrix>()) {
-        if (ctor->Parameters().size() == 1) {
-            // Matrix constructor with single scalar.
-            auto fn = utils::GetOrCreate(matrix_scalar_ctors_, mat, [&]() -> std::string {
-                TextBuffer b;
-                TINT_DEFER(helpers_.Append(b));
-
-                auto name = UniqueIdentifier("build_mat" + std::to_string(mat->columns()) + "x" +
-                                             std::to_string(mat->rows()));
-                {
-                    auto l = line(&b);
-                    if (!EmitType(l, mat, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
-                        return "";
-                    }
-                    l << " " << name << "(";
-                    if (!EmitType(l, mat->type(), ast::StorageClass::kNone, ast::Access::kUndefined,
-                                  "")) {
-                        return "";
-                    }
-                    l << " value) {";
-                }
-                {
-                    ScopedIndent si(&b);
-                    auto l = line(&b);
-                    l << "return ";
-                    if (!EmitType(l, mat, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
-                        return "";
-                    }
-                    l << "(";
-                    for (uint32_t i = 0; i < mat->columns() * mat->rows(); i++) {
-                        l << ((i > 0) ? ", value" : "value");
-                    }
-                    l << ");";
-                }
-                line(&b) << "}";
-                return name;
-            });
-            if (fn.empty()) {
-                return false;
-            }
-            out << fn << "(";
-            if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
-                return false;
-            }
-            out << ")";
-            return true;
+    // Single parameter matrix initializers must be identity constructor.
+    // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
+    if (type->Is<sem::Matrix>() && call->Arguments().size() == 1) {
+        if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
+            TINT_UNREACHABLE(Writer, diagnostics_)
+                << "found a single-parameter matrix constructor that is not identity constructor";
+            return false;
         }
     }
 
@@ -1217,9 +1182,9 @@
     // If true, use scalar_offset_value, otherwise use scalar_offset_expr
     bool scalar_offset_constant = false;
 
-    if (auto val = offset_arg->ConstantValue()) {
-        TINT_ASSERT(Writer, val.Type()->Is<sem::U32>());
-        scalar_offset_value = static_cast<uint32_t>(val.Element<AInt>(0).value);
+    if (auto* val = offset_arg->ConstantValue()) {
+        TINT_ASSERT(Writer, val->Type()->Is<sem::U32>());
+        scalar_offset_value = static_cast<uint32_t>(std::get<AInt>(val->Value()));
         scalar_offset_value /= 4;  // bytes -> scalar index
         scalar_offset_constant = true;
     }
@@ -1779,8 +1744,8 @@
 
             {  // T compare_value = <compare_value>;
                 auto pre = line();
-                if (!EmitTypeAndName(pre, TypeOf(compare_value), ast::StorageClass::kNone,
-                                     ast::Access::kUndefined, compare)) {
+                if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
+                                     ast::StorageClass::kNone, ast::Access::kUndefined, compare)) {
                     return false;
                 }
                 pre << " = ";
@@ -2143,7 +2108,7 @@
     // Returns the argument with the given usage
     auto arg = [&](Usage usage) {
         int idx = signature.IndexOf(usage);
-        return (idx >= 0) ? arguments[idx] : nullptr;
+        return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
     };
 
     auto* texture = arg(Usage::kTexture);
@@ -2373,7 +2338,7 @@
         case sem::BuiltinType::kTextureGather:
             out << ".Gather";
             if (builtin->Parameters()[0]->Usage() == sem::ParameterUsage::kComponent) {
-                switch (call->Arguments()[0]->ConstantValue().Element<AInt>(0).value) {
+                switch (call->Arguments()[0]->ConstantValue()->As<AInt>()) {
                     case 0:
                         out << "Red";
                         break;
@@ -2420,8 +2385,9 @@
         auto* i32 = builder_.create<sem::I32>();
         auto* zero = builder_.Expr(0_i);
         auto* stmt = builder_.Sem().Get(vector)->Stmt();
-        builder_.Sem().Add(zero, builder_.create<sem::Expression>(zero, i32, stmt, sem::Constant{},
-                                                                  /* has_side_effects */ false));
+        builder_.Sem().Add(
+            zero, builder_.create<sem::Expression>(zero, i32, stmt, /* constant_value */ nullptr,
+                                                   /* has_side_effects */ false));
         auto* packed = AppendVector(&builder_, vector, zero);
         return EmitExpression(out, packed->Declaration());
     };
@@ -2650,7 +2616,7 @@
 
 bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) {
     if (auto* sem = builder_.Sem().Get(expr)) {
-        if (auto constant = sem->ConstantValue()) {
+        if (auto* constant = sem->ConstantValue()) {
             return EmitConstant(out, constant);
         }
     }
@@ -2882,8 +2848,10 @@
                     return false;
             }
         },
-        [&](const ast::Let* let) { return EmitProgramConstVariable(let); },
         [&](const ast::Override* override) { return EmitOverride(override); },
+        [&](const ast::Const*) {
+            return true;  // Constants are embedded at their use
+        },
         [&](Default) {
             TINT_ICE(Writer, diagnostics_)
                 << "unhandled global variable type " << global->TypeInfo().name;
@@ -3073,7 +3041,7 @@
             // Emit the workgroup_size attribute.
             auto wgsize = func_sem->WorkgroupSize();
             out << "[numthreads(";
-            for (int i = 0; i < 3; i++) {
+            for (size_t i = 0; i < 3; i++) {
                 if (i > 0) {
                     out << ", ";
                 }
@@ -3143,107 +3111,101 @@
     return true;
 }
 
-bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant& constant) {
-    auto emit_bool = [&](size_t element_idx) {
-        out << (constant.Element<AInt>(element_idx) ? "true" : "false");
-        return true;
-    };
-    auto emit_f32 = [&](size_t element_idx) {
-        PrintF32(out, static_cast<float>(constant.Element<AFloat>(element_idx)));
-        return true;
-    };
-    auto emit_i32 = [&](size_t element_idx) {
-        out << constant.Element<AInt>(element_idx).value;
-        return true;
-    };
-    auto emit_u32 = [&](size_t element_idx) {
-        out << constant.Element<AInt>(element_idx).value << "u";
-        return true;
-    };
-    auto emit_vector = [&](const sem::Vector* vec_ty, size_t start, size_t end) {
-        if (constant.AllEqual(start, end)) {
-            {
-                ScopedParen sp(out);
-                bool ok = Switch(
-                    vec_ty->type(),                                      //
-                    [&](const sem::Bool*) { return emit_bool(start); },  //
-                    [&](const sem::F32*) { return emit_f32(start); },    //
-                    [&](const sem::I32*) { return emit_i32(start); },    //
-                    [&](const sem::U32*) { return emit_u32(start); }     //
-                );
-                if (!ok) {
-                    return false;
-                }
-            }
-            out << ".";
-            for (size_t i = start; i < end; i++) {
-                out << "x";
-            }
+bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) {
+    return Switch(
+        constant->Type(),  //
+        [&](const sem::Bool*) {
+            out << (constant->As<AInt>() ? "true" : "false");
             return true;
-        }
+        },
+        [&](const sem::F32*) {
+            PrintF32(out, constant->As<float>());
+            return true;
+        },
+        [&](const sem::I32*) {
+            out << constant->As<AInt>();
+            return true;
+        },
+        [&](const sem::U32*) {
+            out << constant->As<AInt>() << "u";
+            return true;
+        },
+        [&](const sem::Vector* v) {
+            if (constant->AllEqual()) {
+                {
+                    ScopedParen sp(out);
+                    if (!EmitConstant(out, constant->Index(0))) {
+                        return false;
+                    }
+                }
+                out << ".";
+                for (size_t i = 0; i < v->Width(); i++) {
+                    out << "x";
+                }
+                return true;
+            }
 
-        if (!EmitType(out, vec_ty, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
-            return false;
-        }
+            if (!EmitType(out, v, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                return false;
+            }
 
-        ScopedParen sp(out);
+            ScopedParen sp(out);
 
-        auto emit_els = [&](auto emit_el) {
-            for (size_t i = start; i < end; i++) {
-                if (i > start) {
+            for (size_t i = 0; i < v->Width(); i++) {
+                if (i > 0) {
                     out << ", ";
                 }
-                if (!emit_el(i)) {
+                if (!EmitConstant(out, constant->Index(i))) {
                     return false;
                 }
             }
             return true;
-        };
-        return Switch(
-            vec_ty->type(),                                         //
-            [&](const sem::Bool*) { return emit_els(emit_bool); },  //
-            [&](const sem::F32*) { return emit_els(emit_f32); },    //
-            [&](const sem::I32*) { return emit_els(emit_i32); },    //
-            [&](const sem::U32*) { return emit_els(emit_u32); },    //
-            [&](Default) {
-                diagnostics_.add_error(diag::System::Writer,
-                                       "unhandled constant vector element type: " +
-                                           builder_.FriendlyName(vec_ty->type()));
-                return false;
-            });
-    };
-    auto emit_matrix = [&](const sem::Matrix* m) {
-        if (!EmitType(out, constant.Type(), ast::StorageClass::kNone, ast::Access::kUndefined,
-                      "")) {
-            return false;
-        }
-
-        ScopedParen sp(out);
-
-        for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) {
-            if (column_idx > 0) {
-                out << ", ";
-            }
-            size_t start = m->rows() * column_idx;
-            size_t end = m->rows() * (column_idx + 1);
-            if (!emit_vector(m->ColumnType(), start, end)) {
+        },
+        [&](const sem::Matrix* m) {
+            if (!EmitType(out, m, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
-        }
-        return true;
-    };
-    return Switch(
-        constant.Type(),                                                                   //
-        [&](const sem::Bool*) { return emit_bool(0); },                                    //
-        [&](const sem::F32*) { return emit_f32(0); },                                      //
-        [&](const sem::I32*) { return emit_i32(0); },                                      //
-        [&](const sem::U32*) { return emit_u32(0); },                                      //
-        [&](const sem::Vector* v) { return emit_vector(v, 0, constant.ElementCount()); },  //
-        [&](const sem::Matrix* m) { return emit_matrix(m); },
+
+            ScopedParen sp(out);
+
+            for (size_t i = 0; i < m->columns(); i++) {
+                if (i > 0) {
+                    out << ", ";
+                }
+                if (!EmitConstant(out, constant->Index(i))) {
+                    return false;
+                }
+            }
+            return true;
+        },
+        [&](const sem::Array* a) {
+            if (constant->AllZero()) {
+                out << "(";
+                if (!EmitType(out, a, ast::StorageClass::kNone, ast::Access::kUndefined, "")) {
+                    return false;
+                }
+                out << ")0";
+                return true;
+            }
+
+            out << "{";
+            TINT_DEFER(out << "}");
+
+            for (size_t i = 0; i < a->Count(); i++) {
+                if (i > 0) {
+                    out << ", ";
+                }
+                if (!EmitConstant(out, constant->Index(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
         [&](Default) {
             diagnostics_.add_error(
                 diag::System::Writer,
-                "unhandled constant type: " + builder_.FriendlyName(constant.Type()));
+                "unhandled constant type: " + builder_.FriendlyName(constant->Type()));
             return false;
         });
 }
@@ -3618,6 +3580,9 @@
                 v->variable,  //
                 [&](const ast::Var* var) { return EmitVar(var); },
                 [&](const ast::Let* let) { return EmitLet(let); },
+                [&](const ast::Const*) {
+                    return true;  // Constants are embedded at their use
+                },
                 [&](Default) {  //
                     TINT_ICE(Writer, diagnostics_)
                         << "unknown variable type: " << v->variable->TypeInfo().name;
@@ -3728,9 +3693,8 @@
             while (auto* arr = base_type->As<sem::Array>()) {
                 if (arr->IsRuntimeSized()) {
                     TINT_ICE(Writer, diagnostics_)
-                        << "Runtime arrays may only exist in storage buffers, which "
-                           "should "
-                           "have been transformed into a ByteAddressBuffer";
+                        << "Runtime arrays may only exist in storage buffers, which should have "
+                           "been transformed into a ByteAddressBuffer";
                     return false;
                 }
                 sizes.push_back(arr->Count());
@@ -4074,25 +4038,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitProgramConstVariable(const ast::Let* let) {
-    auto* sem = builder_.Sem().Get(let);
-    auto* type = sem->Type();
-
-    auto out = line();
-    out << "static const ";
-    if (!EmitTypeAndName(out, type, ast::StorageClass::kNone, ast::Access::kUndefined,
-                         builder_.Symbols().NameFor(let->symbol))) {
-        return false;
-    }
-    out << " = ";
-    if (!EmitExpression(out, let->constructor)) {
-        return false;
-    }
-    out << ";";
-
-    return true;
-}
-
 bool GeneratorImpl::EmitOverride(const ast::Override* override) {
     auto* sem = builder_.Sem().Get(override);
     auto* type = sem->Type();
diff --git a/src/tint/writer/hlsl/generator_impl.h b/src/tint/writer/hlsl/generator_impl.h
index 485b779..bf2debe 100644
--- a/src/tint/writer/hlsl/generator_impl.h
+++ b/src/tint/writer/hlsl/generator_impl.h
@@ -93,7 +93,8 @@
     /// @param stmt the statement to emit
     /// @returns true if the statement was emitted successfully
     bool EmitAssign(const ast::AssignmentStatement* stmt);
-    /// Emits code such that if `expr` is zero, it emits one, else `expr`
+    /// Emits code such that if `expr` is zero, it emits one, else `expr`.
+    /// Used to avoid divide-by-zeros by substituting constant zeros with ones.
     /// @param out the output of the expression stream
     /// @param expr the expression
     /// @returns true if the expression was emitted, false otherwise
@@ -342,7 +343,7 @@
     /// @param out the output stream
     /// @param constant the constant value to emit
     /// @returns true if the constant value was successfully emitted
-    bool EmitConstant(std::ostream& out, const sem::Constant& constant);
+    bool EmitConstant(std::ostream& out, const sem::Constant* constant);
     /// Handles a literal
     /// @param out the output stream
     /// @param lit the literal to emit
@@ -448,10 +449,6 @@
     /// @param let the variable to generate
     /// @returns true if the variable was emitted
     bool EmitLet(const ast::Let* let);
-    /// Handles generating a module-scope 'let' declaration
-    /// @param let the 'let' to emit
-    /// @returns true if the variable was emitted
-    bool EmitProgramConstVariable(const ast::Let* let);
     /// Handles generating a module-scope 'override' declaration
     /// @param override the 'override' to emit
     /// @returns true if the variable was emitted
diff --git a/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
index bbdeb10..dfa3d67 100644
--- a/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_array_accessor_test.cc
@@ -22,7 +22,7 @@
 using HlslGeneratorImplTest_Expression = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Expression, IndexAccessor) {
-    Global("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
     auto* expr = IndexAccessor("ary", 5_i);
     WrapInFunction(expr);
 
diff --git a/src/tint/writer/hlsl/generator_impl_assign_test.cc b/src/tint/writer/hlsl/generator_impl_assign_test.cc
index c69cbdf..57e65a9 100644
--- a/src/tint/writer/hlsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_assign_test.cc
@@ -41,7 +41,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_ConstantIndex) {
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_LetIndex) {
     Func("fn", {}, ty.void_(),
          {
              Decl(Var("lhs", ty.vec3<f32>())),
@@ -54,10 +54,35 @@
 
     ASSERT_TRUE(gen.Generate());
     EXPECT_EQ(gen.result(),
-              R"(void fn() {
+              R"(void set_float3(inout float3 vec, int idx, float val) {
+  vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;
+}
+
+void fn() {
   float3 lhs = float3(0.0f, 0.0f, 0.0f);
   float rhs = 0.0f;
   const uint index = 0u;
+  set_float3(lhs, index, rhs);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Vector_Assign_ConstIndex) {
+    Func("fn", {}, ty.void_(),
+         {
+             Decl(Var("lhs", ty.vec3<f32>())),
+             Decl(Var("rhs", ty.f32())),
+             Decl(Const("index", ty.u32(), Expr(0_u))),
+             Assign(IndexAccessor("lhs", "index"), "rhs"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate());
+    EXPECT_EQ(gen.result(),
+              R"(void fn() {
+  float3 lhs = float3(0.0f, 0.0f, 0.0f);
+  float rhs = 0.0f;
   lhs[0u] = rhs;
 }
 )");
@@ -89,7 +114,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_ConstantIndex) {
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_LetIndex) {
     Func("fn", {}, ty.void_(),
          {
              Decl(Var("lhs", ty.mat4x2<f32>())),
@@ -102,10 +127,40 @@
 
     ASSERT_TRUE(gen.Generate());
     EXPECT_EQ(gen.result(),
-              R"(void fn() {
+              R"(void set_vector_float4x2(inout float4x2 mat, int col, float2 val) {
+  switch (col) {
+    case 0: mat[0] = val; break;
+    case 1: mat[1] = val; break;
+    case 2: mat[2] = val; break;
+    case 3: mat[3] = val; break;
+  }
+}
+
+void fn() {
   float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
   float2 rhs = float2(0.0f, 0.0f);
   const uint index = 0u;
+  set_vector_float4x2(lhs, index, rhs);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Vector_ConstIndex) {
+    Func("fn", {}, ty.void_(),
+         {
+             Decl(Var("lhs", ty.mat4x2<f32>())),
+             Decl(Var("rhs", ty.vec2<f32>())),
+             Decl(Const("index", ty.u32(), Expr(0_u))),
+             Assign(IndexAccessor("lhs", "index"), "rhs"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate());
+    EXPECT_EQ(gen.result(),
+              R"(void fn() {
+  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+  float2 rhs = float2(0.0f, 0.0f);
   lhs[0u] = rhs;
 }
 )");
@@ -142,7 +197,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_ConstantIndex) {
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_LetIndex) {
     Func("fn", {}, ty.void_(),
          {
              Decl(Var("lhs", ty.mat4x2<f32>())),
@@ -155,10 +210,48 @@
 
     ASSERT_TRUE(gen.Generate());
     EXPECT_EQ(gen.result(),
-              R"(void fn() {
+              R"(void set_scalar_float4x2(inout float4x2 mat, int col, int row, float val) {
+  switch (col) {
+    case 0:
+      mat[0] = (row.xx == int2(0, 1)) ? val.xx : mat[0];
+      break;
+    case 1:
+      mat[1] = (row.xx == int2(0, 1)) ? val.xx : mat[1];
+      break;
+    case 2:
+      mat[2] = (row.xx == int2(0, 1)) ? val.xx : mat[2];
+      break;
+    case 3:
+      mat[3] = (row.xx == int2(0, 1)) ? val.xx : mat[3];
+      break;
+  }
+}
+
+void fn() {
   float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
   float rhs = 0.0f;
   const uint index = 0u;
+  set_scalar_float4x2(lhs, index, index, rhs);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Assign, Emit_Matrix_Assign_Scalar_ConstIndex) {
+    Func("fn", {}, ty.void_(),
+         {
+             Decl(Var("lhs", ty.mat4x2<f32>())),
+             Decl(Var("rhs", ty.f32())),
+             Decl(Const("index", ty.u32(), Expr(0_u))),
+             Assign(IndexAccessor(IndexAccessor("lhs", "index"), "index"), "rhs"),
+         });
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate());
+    EXPECT_EQ(gen.result(),
+              R"(void fn() {
+  float4x2 lhs = float4x2(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+  float rhs = 0.0f;
   lhs[0u][0u] = rhs;
 }
 )");
diff --git a/src/tint/writer/hlsl/generator_impl_binary_test.cc b/src/tint/writer/hlsl/generator_impl_binary_test.cc
index 5825536..b5e47f7 100644
--- a/src/tint/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_binary_test.cc
@@ -50,8 +50,8 @@
         return;
     }
 
-    Global("left", ty.f32(), ast::StorageClass::kPrivate);
-    Global("right", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("right", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -73,8 +73,8 @@
         return;
     }
 
-    Global("left", ty.u32(), ast::StorageClass::kPrivate);
-    Global("right", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("right", ty.u32(), ast::StorageClass::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -101,8 +101,8 @@
         return;
     }
 
-    Global("left", ty.i32(), ast::StorageClass::kPrivate);
-    Global("right", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("left", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("right", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* left = Expr("left");
     auto* right = Expr("right");
@@ -171,7 +171,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixScalar) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = Expr(1_f);
 
@@ -186,7 +186,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarMatrix) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = Expr(1_f);
     auto* rhs = Expr("mat");
 
@@ -201,7 +201,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixVector) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = Expr("mat");
     auto* rhs = vec3<f32>(1_f, 1_f, 1_f);
 
@@ -216,7 +216,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_VectorMatrix) {
-    Global("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("mat", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* lhs = vec3<f32>(1_f, 1_f, 1_f);
     auto* rhs = Expr("mat");
 
@@ -231,8 +231,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixMatrix) {
-    Global("lhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
-    Global("rhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kMultiply, Expr("lhs"), Expr("rhs"));
     WrapInFunction(expr);
@@ -245,8 +245,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_And) {
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -265,10 +265,10 @@
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_Multi) {
     // (a && b) || (c || d)
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -297,8 +297,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Logical_Or) {
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
     WrapInFunction(expr);
@@ -324,9 +324,9 @@
     //   return 3i;
     // }
 
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr =
         If(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b")),
@@ -361,9 +361,9 @@
 TEST_F(HlslGeneratorImplTest_Binary, Return_WithLogical) {
     // return (a && b) || c;
 
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr = Return(create<ast::BinaryExpression>(
         ast::BinaryOp::kLogicalOr,
@@ -389,10 +389,10 @@
 TEST_F(HlslGeneratorImplTest_Binary, Assign_WithLogical) {
     // a = (b || c) && d;
 
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* expr =
         Assign(Expr("a"),
@@ -420,9 +420,9 @@
 TEST_F(HlslGeneratorImplTest_Binary, Decl_WithLogical) {
     // var a : bool = (b && c) || d;
 
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* var =
         Var("a", ty.bool_(), ast::StorageClass::kNone,
@@ -459,10 +459,10 @@
              Param(Sym(), ty.bool_()),
          },
          ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("b", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("c", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("d", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("d", ty.bool_(), ast::StorageClass::kPrivate);
 
     ast::ExpressionList params;
     params.push_back(
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index c497e51..d008ab8 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -152,13 +152,13 @@
 TEST_P(HlslBuiltinTest, Emit) {
     auto param = GetParam();
 
-    Global("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    Global("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
-    Global("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    Global("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    Global("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
-    Global("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = GenerateCall(param.builtin, param.type, this);
     ASSERT_NE(nullptr, call) << "Unhandled builtin";
@@ -241,8 +241,8 @@
 TEST_F(HlslGeneratorImplTest_Builtin, Builtin_Call) {
     auto* call = Call("dot", "param1", "param2");
 
-    Global("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     WrapInFunction(CallStmt(call));
 
@@ -466,7 +466,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack4x8Snorm) {
     auto* call = Call("pack4x8snorm", "p1");
-    Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -488,7 +488,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack4x8Unorm) {
     auto* call = Call("pack4x8unorm", "p1");
-    Global("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec4<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -510,7 +510,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack2x16Snorm) {
     auto* call = Call("pack2x16snorm", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -532,7 +532,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack2x16Unorm) {
     auto* call = Call("pack2x16unorm", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -554,7 +554,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Pack2x16Float) {
     auto* call = Call("pack2x16float", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -576,7 +576,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack4x8Snorm) {
     auto* call = Call("unpack4x8snorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -599,7 +599,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack4x8Unorm) {
     auto* call = Call("unpack4x8unorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -622,7 +622,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack2x16Snorm) {
     auto* call = Call("unpack2x16snorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -645,7 +645,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack2x16Unorm) {
     auto* call = Call("unpack2x16unorm", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
@@ -668,7 +668,7 @@
 
 TEST_F(HlslGeneratorImplTest_Builtin, Unpack2x16Float) {
     auto* call = Call("unpack2x16float", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/hlsl/generator_impl_call_test.cc b/src/tint/writer/hlsl/generator_impl_call_test.cc
index b72b0eb..fc41a81 100644
--- a/src/tint/writer/hlsl/generator_impl_call_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_call_test.cc
@@ -42,8 +42,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.f32(), {Return(1.23_f)});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -62,8 +62,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = CallStmt(Call("my_func", "param1", "param2"));
     WrapInFunction(call);
diff --git a/src/tint/writer/hlsl/generator_impl_case_test.cc b/src/tint/writer/hlsl/generator_impl_case_test.cc
index ee3acfc..5c7e427 100644
--- a/src/tint/writer/hlsl/generator_impl_case_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_case_test.cc
@@ -99,7 +99,7 @@
 
     gen.increment_indent();
 
-    ASSERT_TRUE(gen.EmitCase(s, 0_i)) << gen.error();
+    ASSERT_TRUE(gen.EmitCase(s, 0u)) << gen.error();
     EXPECT_EQ(gen.result(), R"(  default: {
     break;
   }
diff --git a/src/tint/writer/hlsl/generator_impl_constructor_test.cc b/src/tint/writer/hlsl/generator_impl_constructor_test.cc
index 700d94b..6a1d696 100644
--- a/src/tint/writer/hlsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_constructor_test.cc
@@ -223,6 +223,24 @@
     EXPECT_THAT(gen.result(), HasSubstr("float2x3 tint_symbol = float2x3((0.0f).xxx, (0.0f).xxx)"));
 }
 
+TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Identity) {
+    // fn f() {
+    //     var m_1: mat4x4<f32> = mat4x4<f32>();
+    //     var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
+    // }
+
+    auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), mat4x4<f32>());
+    auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), mat4x4<f32>(m_1));
+
+    WrapInFunction(m_1, m_2);
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_THAT(gen.result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
+}
+
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Array) {
     WrapInFunction(Construct(ty.array(ty.vec3<f32>(), 3_u), vec3<f32>(1_f, 2_f, 3_f),
                              vec3<f32>(4_f, 5_f, 6_f), vec3<f32>(7_f, 8_f, 9_f)));
diff --git a/src/tint/writer/hlsl/generator_impl_function_test.cc b/src/tint/writer/hlsl/generator_impl_function_test.cc
index dac42d0..85647a5 100644
--- a/src/tint/writer/hlsl/generator_impl_function_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_function_test.cc
@@ -346,11 +346,11 @@
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_Uniform) {
     auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())});
-    auto* ubo = Global("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(1),
-                       });
+    auto* ubo = GlobalVar("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(1u),
+                          });
 
     Func("sub_func",
          {
@@ -393,11 +393,11 @@
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_EntryPoint_With_UniformStruct) {
     auto* s = Structure("Uniforms", {Member("coord", ty.vec4<f32>())});
 
-    Global("uniforms", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("uniforms", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone,
                     MemberAccessor(MemberAccessor("uniforms", "coord"), "x"));
@@ -431,11 +431,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
@@ -467,11 +467,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
@@ -503,11 +503,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("frag_main", {}, ty.void_(),
          {
@@ -537,11 +537,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("frag_main", {}, ty.void_(),
          {
@@ -567,11 +567,11 @@
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
     auto* s = Structure("S", {Member("x", ty.f32())});
-    Global("coord", ty.Of(s), ast::StorageClass::kUniform,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kUniform,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("sub_func",
          {
@@ -613,11 +613,11 @@
 
 TEST_F(HlslGeneratorImplTest_Function, Emit_Attribute_Called_By_EntryPoint_With_StorageBuffer) {
     auto* s = Structure("S", {Member("x", ty.f32())});
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(1),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(1u),
+              });
 
     Func("sub_func",
          {
@@ -718,11 +718,7 @@
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_EQ(gen.result(), R"(static const int width = 2;
-static const int height = 3;
-static const int depth = 4;
-
-[numthreads(2, 3, 4)]
+    EXPECT_EQ(gen.result(), R"([numthreads(2, 3, 4)]
 void main() {
   return;
 }
@@ -863,11 +859,11 @@
 
     auto* s = Structure("Data", {Member("d", ty.f32())});
 
-    Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
diff --git a/src/tint/writer/hlsl/generator_impl_identifier_test.cc b/src/tint/writer/hlsl/generator_impl_identifier_test.cc
index d982e1e..d4b645f 100644
--- a/src/tint/writer/hlsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_identifier_test.cc
@@ -20,7 +20,7 @@
 using HlslGeneratorImplTest_Identifier = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Identifier, EmitIdentifierExpression) {
-    Global("foo", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("foo", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* i = Expr("foo");
     WrapInFunction(i);
diff --git a/src/tint/writer/hlsl/generator_impl_if_test.cc b/src/tint/writer/hlsl/generator_impl_if_test.cc
index 1668d71..3195a06 100644
--- a/src/tint/writer/hlsl/generator_impl_if_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_if_test.cc
@@ -20,7 +20,7 @@
 using HlslGeneratorImplTest_If = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_If, Emit_If) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* cond = Expr("cond");
     auto* body = Block(Return());
@@ -38,8 +38,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElseIf) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_cond = Expr("else_cond");
     auto* else_body = Block(Return());
@@ -65,7 +65,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithElse) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_body = Block(Return());
 
@@ -88,8 +88,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_If, Emit_IfWithMultiple) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_cond = Expr("else_cond");
 
diff --git a/src/tint/writer/hlsl/generator_impl_import_test.cc b/src/tint/writer/hlsl/generator_impl_import_test.cc
index f7d544e..d6fdeac 100644
--- a/src/tint/writer/hlsl/generator_impl_import_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_import_test.cc
@@ -259,7 +259,7 @@
                          testing::Values(HlslImportData{"clamp", "clamp"}));
 
 TEST_F(HlslGeneratorImplTest_Import, HlslImportData_Determinant) {
-    Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("determinant", "var");
     WrapInFunction(expr);
diff --git a/src/tint/writer/hlsl/generator_impl_loop_test.cc b/src/tint/writer/hlsl/generator_impl_loop_test.cc
index 29d1822..9f288f6 100644
--- a/src/tint/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_loop_test.cc
@@ -66,8 +66,8 @@
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    Global("lhs", ty.f32(), ast::StorageClass::kPrivate);
-    Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* body = Block(create<ast::DiscardStatement>());
     auto* continuing = Block(CallStmt(Call("a_statement")));
@@ -112,7 +112,7 @@
     //   }
     // }
 
-    Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.4_f))),  //
                        Decl(Var("other", ty.f32())),             //
diff --git a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
index ffa64fd..8149b7f 100644
--- a/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -91,11 +91,11 @@
 
         auto* s = b.Structure("Data", members);
 
-        b.Global("data", b.ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                 ast::AttributeList{
-                     b.create<ast::BindingAttribute>(0),
-                     b.create<ast::GroupAttribute>(1),
-                 });
+        b.GlobalVar("data", b.ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                    ast::AttributeList{
+                        b.create<ast::BindingAttribute>(0u),
+                        b.create<ast::GroupAttribute>(1u),
+                    });
     }
 
     void SetupFunction(ast::StatementList statements) {
@@ -115,7 +115,7 @@
 
 TEST_F(HlslGeneratorImplTest_MemberAccessor, EmitExpression_MemberAccessor) {
     auto* s = Structure("Data", {Member("mem", ty.f32())});
-    Global("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
 
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(Var("expr", ty.f32(), ast::StorageClass::kNone, expr));
diff --git a/src/tint/writer/hlsl/generator_impl_module_constant_test.cc b/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
index 19be5e6..3a988a0 100644
--- a/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_module_constant_test.cc
@@ -22,17 +22,180 @@
 
 using HlslGeneratorImplTest_ModuleConstant = TestHelper;
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_ModuleConstant) {
-    auto* var = Let("pos", ty.array<f32, 3>(), array<f32, 3>(1_f, 2_f, 3_f));
-    WrapInFunction(Decl(var));
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AInt) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.error();
-    EXPECT_EQ(gen.result(), "static const float pos[3] = {1.0f, 2.0f, 3.0f};\n");
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
 }
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_i32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_u32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const uint l = 1u;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_f32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int3 l = int3(1, 2, 3);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_f32) {
+    auto* var = GlobalConst("G", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_f32) {
+    auto* var = GlobalConst("G", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l[3] = {1.0f, 2.0f, 3.0f};
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_vec2_bool) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                                      vec2<bool>(true, false),         //
+                                      vec2<bool>(false, true),         //
+                                      vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_Override) {
     auto* var = Override("pos", ty.f32(), Expr(3_f),
                          ast::AttributeList{
                              Id(23),
@@ -48,7 +211,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoConstructor) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_Override_NoConstructor) {
     auto* var = Override("pos", ty.f32(), nullptr,
                          ast::AttributeList{
                              Id(23),
@@ -64,7 +227,7 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoId) {
+TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_Override_NoId) {
     auto* a = Override("a", ty.f32(), Expr(3_f),
                        ast::AttributeList{
                            Id(0),
diff --git a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
index e7b81e4..3bb1cec 100644
--- a/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_sanitizer_test.cc
@@ -26,11 +26,11 @@
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -64,11 +64,11 @@
                                          Member(0, "z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -100,11 +100,11 @@
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
@@ -140,16 +140,16 @@
 
 TEST_F(HlslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
-    Global("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(2),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
+    GlobalVar("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(2u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -188,12 +188,11 @@
 
 TEST_F(HlslSanitizerTest, PromoteArrayInitializerToConstVar) {
     auto* array_init = array<i32, 4>(1_i, 2_i, 3_i, 4_i);
-    auto* array_index = IndexAccessor(array_init, 3_i);
-    auto* pos = Var("pos", ty.i32(), ast::StorageClass::kNone, array_index);
 
     Func("main", {}, ty.void_(),
          {
-             Decl(pos),
+             Decl(Var("idx", nullptr, Expr(3_i))),
+             Decl(Var("pos", ty.i32(), IndexAccessor(array_init, "idx"))),
          },
          {
              Stage(ast::PipelineStage::kFragment),
@@ -205,8 +204,9 @@
 
     auto got = gen.result();
     auto* expect = R"(void main() {
+  int idx = 3;
   const int tint_symbol[4] = {1, 2, 3, 4};
-  int pos = tint_symbol[3];
+  int pos = tint_symbol[idx];
   return;
 }
 )";
diff --git a/src/tint/writer/hlsl/generator_impl_switch_test.cc b/src/tint/writer/hlsl/generator_impl_switch_test.cc
index 3ef1a7b..7d89672 100644
--- a/src/tint/writer/hlsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_switch_test.cc
@@ -22,7 +22,7 @@
 using HlslGeneratorImplTest_Switch = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch) {
-    Global("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
     auto* s = Switch(                     //
         Expr("cond"),                     //
         Case(Expr(5_i), Block(Break())),  //
@@ -46,8 +46,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Switch, Emit_Switch_OnlyDefaultCase) {
-    Global("cond", ty.i32(), ast::StorageClass::kPrivate);
-    Global("a", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
     auto* s = Switch(  //
         Expr("cond"),  //
         DefaultCase(Block(Assign(Expr("a"), Expr(42_i)))));
diff --git a/src/tint/writer/hlsl/generator_impl_type_test.cc b/src/tint/writer/hlsl/generator_impl_type_test.cc
index b99f06e..614d8f1 100644
--- a/src/tint/writer/hlsl/generator_impl_type_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_type_test.cc
@@ -33,7 +33,7 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array) {
     auto* arr = ty.array<bool, 4>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -46,7 +46,7 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
     auto* arr = ty.array(ty.array<bool, 4>(), 5_u);
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -59,7 +59,7 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) {
     auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5_u), 6_u);
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -72,7 +72,7 @@
 
 TEST_F(HlslGeneratorImplTest_Type, EmitType_Array_WithoutName) {
     auto* arr = ty.array<bool, 4>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -134,7 +134,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -153,11 +153,11 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -170,7 +170,7 @@
                                  Member("a", ty.i32()),
                                  Member("b", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -186,7 +186,7 @@
                                  Member("double", ty.i32()),
                                  Member("float", ty.f32()),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = SanitizeAndBuild();
 
@@ -203,7 +203,7 @@
                                  Member("a", ty.i32(), {MemberOffset(0)}),
                                  Member("b", ty.f32(), {MemberOffset(8)}),
                              });
-    Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("g", ty.Of(s), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -287,11 +287,11 @@
 
     auto* t = ty.depth_texture(params.dim);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
@@ -317,11 +317,11 @@
 TEST_F(HlslDepthMultisampledTexturesTest, Emit) {
     auto* t = ty.depth_multisampled_texture(ast::TextureDimension::k2d);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
@@ -360,11 +360,11 @@
     }
     auto* t = ty.sampled_texture(params.dim, datatype);
 
-    Global("tex", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("tex", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
@@ -495,7 +495,7 @@
 
     auto* t = ty.storage_texture(params.dim, params.imgfmt, ast::Access::kWrite);
 
-    Global("tex", t, ast::AttributeList{GroupAndBinding(2, 1)});
+    GlobalVar("tex", t, ast::AttributeList{GroupAndBinding(2, 1)});
 
     Func("main", {}, ty.void_(), {CallStmt(Call("textureDimensions", "tex"))},
          {Stage(ast::PipelineStage::kFragment)});
diff --git a/src/tint/writer/hlsl/generator_impl_unary_op_test.cc b/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
index c9abf1f..74320ea 100644
--- a/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using HlslUnaryOpTest = TestHelper;
 
 TEST_F(HlslUnaryOpTest, AddressOf) {
-    Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Complement) {
-    Global("expr", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.u32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Indirection) {
-    Global("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
     auto* p =
         Let("expr", nullptr, create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
@@ -58,7 +58,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Not) {
-    Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -70,7 +70,7 @@
 }
 
 TEST_F(HlslUnaryOpTest, Negation) {
-    Global("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index 5ed85f3..1e111cd 100644
--- a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -16,6 +16,8 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/writer/hlsl/test_helper.h"
 
+using namespace tint::number_suffixes;  // NOLINT
+
 namespace tint::writer::hlsl {
 namespace {
 
@@ -36,7 +38,7 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
-TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Let) {
     auto* var = Let("a", ty.f32(), Construct(ty.f32()));
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -49,6 +51,192 @@
     EXPECT_EQ(gen.result(), "  const float a = 0.0f;\n");
 }
 
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
+    auto* var = Const("a", ty.f32(), Construct(ty.f32()));
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int l = 1;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const uint l = 1u;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l = 1.0f;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const int3 l = int3(1, 2, 3);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float3 l = float3(1.0f, 2.0f, 3.0f);
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float2x3 l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const float l[3] = {1.0f, 2.0f, 3.0f};
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(void f() {
+  const bool2 l[3] = {bool2(true, false), bool2(false, true), (true).xx};
+}
+)");
+}
+
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
     auto* var = Var("a", ty.array<f32, 5>());
 
@@ -63,7 +251,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Private) {
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     WrapInFunction(Expr("a"));
 
@@ -75,19 +263,6 @@
     EXPECT_THAT(gen.result(), HasSubstr("  static float a = 0.0f;\n"));
 }
 
-TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_Private) {
-    Global("initializer", ty.f32(), ast::StorageClass::kPrivate);
-    Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
-
-    WrapInFunction(Expr("a"));
-
-    GeneratorImpl& gen = Build();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr(R"(float a = initializer;
-)"));
-}
-
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec) {
     auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
 
diff --git a/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc b/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
index 826a545..5de44b1 100644
--- a/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_workgroup_var_test.cc
@@ -27,7 +27,7 @@
 using HlslGeneratorImplTest_WorkgroupVar = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_WorkgroupVar, Basic) {
-    Global("wg", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.f32(), ast::StorageClass::kWorkgroup);
 
     Func("main", {}, ty.void_(), {Assign("wg", 1.2_f)},
          {
@@ -43,7 +43,7 @@
 TEST_F(HlslGeneratorImplTest_WorkgroupVar, Aliased) {
     auto* alias = Alias("F32", ty.f32());
 
-    Global("wg", ty.Of(alias), ast::StorageClass::kWorkgroup);
+    GlobalVar("wg", ty.Of(alias), ast::StorageClass::kWorkgroup);
 
     Func("main", {}, ty.void_(), {Assign("wg", 1.2_f)},
          {
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 1181c02..43324bb 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -64,14 +64,13 @@
 #include "src/tint/transform/expand_compound_assignment.h"
 #include "src/tint/transform/manager.h"
 #include "src/tint/transform/module_scope_var_to_entry_point_param.h"
-#include "src/tint/transform/promote_initializers_to_const_var.h"
+#include "src/tint/transform/promote_initializers_to_let.h"
 #include "src/tint/transform/promote_side_effects_to_decl.h"
 #include "src/tint/transform/remove_phonies.h"
 #include "src/tint/transform/simplify_pointers.h"
 #include "src/tint/transform/unshadow.h"
 #include "src/tint/transform/unwind_discard_functions.h"
 #include "src/tint/transform/vectorize_scalar_matrix_constructors.h"
-#include "src/tint/transform/wrap_arrays_in_structs.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
@@ -152,6 +151,8 @@
 
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
+        polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
         polyfills.first_leading_bit = true;
         polyfills.first_trailing_bit = true;
@@ -205,10 +206,9 @@
     manager.Add<transform::ExpandCompoundAssignment>();
     manager.Add<transform::PromoteSideEffectsToDecl>();
     manager.Add<transform::UnwindDiscardFunctions>();
-    manager.Add<transform::PromoteInitializersToConstVar>();
+    manager.Add<transform::PromoteInitializersToLet>();
 
     manager.Add<transform::VectorizeScalarMatrixConstructors>();
-    manager.Add<transform::WrapArraysInStructs>();
     manager.Add<transform::RemovePhonies>();
     manager.Add<transform::SimplifyPointers>();
     // ArrayLengthFromUniform must come after SimplifyPointers, as
@@ -253,9 +253,8 @@
             [&](const ast::Alias*) {
                 return true;  // folded away by the writer
             },
-            [&](const ast::Let* let) {
-                TINT_DEFER(line());
-                return EmitProgramConstVariable(let);
+            [&](const ast::Const*) {
+                return true;  // Constants are embedded at their use
             },
             [&](const ast::Override* override) {
                 TINT_DEFER(line());
@@ -731,16 +730,36 @@
                                         const sem::TypeConstructor* ctor) {
     auto* type = ctor->ReturnType();
 
-    if (type->IsAnyOf<sem::Array, sem::Struct>()) {
-        out << "{";
-    } else {
-        if (!EmitType(out, type, "")) {
-            return false;
-        }
-        out << "(";
+    const char* terminator = ")";
+    TINT_DEFER(out << terminator);
+
+    bool ok = Switch(
+        type,
+        [&](const sem::Array*) {
+            if (!EmitType(out, type, "")) {
+                return false;
+            }
+            out << "{";
+            terminator = "}";
+            return true;
+        },
+        [&](const sem::Struct*) {
+            out << "{";
+            terminator = "}";
+            return true;
+        },
+        [&](Default) {
+            if (!EmitType(out, type, "")) {
+                return false;
+            }
+            out << "(";
+            return true;
+        });
+    if (!ok) {
+        return false;
     }
 
-    int i = 0;
+    size_t i = 0;
     for (auto* arg : call->Arguments()) {
         if (i > 0) {
             out << ", ";
@@ -760,11 +779,6 @@
         i++;
     }
 
-    if (type->IsAnyOf<sem::Array, sem::Struct>()) {
-        out << "}";
-    } else {
-        out << ")";
-    }
     return true;
 }
 
@@ -906,7 +920,7 @@
     // Returns the argument with the given usage
     auto arg = [&](Usage usage) {
         int idx = signature.IndexOf(usage);
-        return (idx >= 0) ? arguments[idx] : nullptr;
+        return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
     };
 
     auto* texture = arg(Usage::kTexture)->Declaration();
@@ -1187,7 +1201,7 @@
                     break;  // Other texture dimensions don't have an offset
             }
         }
-        auto c = component->ConstantValue().Element<AInt>(0);
+        auto c = component->ConstantValue()->As<AInt>();
         switch (c.value) {
             case 0:
                 out << "component::x";
@@ -1335,9 +1349,12 @@
     std::string out = "";
     switch (builtin->Type()) {
         case sem::BuiltinType::kAcos:
+        case sem::BuiltinType::kAcosh:
         case sem::BuiltinType::kAll:
         case sem::BuiltinType::kAny:
         case sem::BuiltinType::kAsin:
+        case sem::BuiltinType::kAsinh:
+        case sem::BuiltinType::kAtanh:
         case sem::BuiltinType::kAtan:
         case sem::BuiltinType::kAtan2:
         case sem::BuiltinType::kCeil:
@@ -1561,10 +1578,9 @@
             ScopedParen sp(out);
             return EmitZeroValue(out, mat->type());
         },
-        [&](const sem::Array* arr) {
-            out << "{";
-            TINT_DEFER(out << "}");
-            return EmitZeroValue(out, arr->ElemType());
+        [&](const sem::Array*) {
+            out << "{}";
+            return true;
         },
         [&](const sem::Struct*) {
             out << "{}";
@@ -1578,88 +1594,94 @@
         });
 }
 
-bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant& constant) {
-    auto emit_bool = [&](size_t element_idx) {
-        out << (constant.Element<AInt>(element_idx) ? "true" : "false");
-        return true;
-    };
-    auto emit_f32 = [&](size_t element_idx) {
-        PrintF32(out, static_cast<float>(constant.Element<AFloat>(element_idx)));
-        return true;
-    };
-    auto emit_i32 = [&](size_t element_idx) {
-        PrintI32(out, static_cast<int32_t>(constant.Element<AInt>(element_idx).value));
-        return true;
-    };
-    auto emit_u32 = [&](size_t element_idx) {
-        out << constant.Element<AInt>(element_idx).value << "u";
-        return true;
-    };
-    auto emit_vector = [&](const sem::Vector* vec_ty, size_t start, size_t end) {
-        if (!EmitType(out, vec_ty, "")) {
-            return false;
-        }
-
-        ScopedParen sp(out);
-
-        auto emit_els = [&](auto emit_el) {
-            if (constant.AllEqual(start, end)) {
-                return emit_el(start);
+bool GeneratorImpl::EmitConstant(std::ostream& out, const sem::Constant* constant) {
+    return Switch(
+        constant->Type(),  //
+        [&](const sem::Bool*) {
+            out << (constant->As<AInt>() ? "true" : "false");
+            return true;
+        },
+        [&](const sem::F32*) {
+            PrintF32(out, constant->As<float>());
+            return true;
+        },
+        [&](const sem::I32*) {
+            PrintI32(out, constant->As<int32_t>());
+            return true;
+        },
+        [&](const sem::U32*) {
+            out << constant->As<AInt>() << "u";
+            return true;
+        },
+        [&](const sem::Vector* v) {
+            if (!EmitType(out, v, "")) {
+                return false;
             }
-            for (size_t i = start; i < end; i++) {
-                if (i > start) {
+
+            ScopedParen sp(out);
+
+            if (constant->AllEqual()) {
+                if (!EmitConstant(out, constant->Index(0))) {
+                    return false;
+                }
+                return true;
+            }
+
+            for (size_t i = 0; i < v->Width(); i++) {
+                if (i > 0) {
                     out << ", ";
                 }
-                if (!emit_el(i)) {
+                if (!EmitConstant(out, constant->Index(i))) {
                     return false;
                 }
             }
             return true;
-        };
-        return Switch(
-            vec_ty->type(),                                         //
-            [&](const sem::Bool*) { return emit_els(emit_bool); },  //
-            [&](const sem::F32*) { return emit_els(emit_f32); },    //
-            [&](const sem::I32*) { return emit_els(emit_i32); },    //
-            [&](const sem::U32*) { return emit_els(emit_u32); },    //
-            [&](Default) {
-                diagnostics_.add_error(diag::System::Writer,
-                                       "unhandled constant vector element type: " +
-                                           builder_.FriendlyName(vec_ty->type()));
-                return false;
-            });
-    };
-    auto emit_matrix = [&](const sem::Matrix* m) {
-        if (!EmitType(out, constant.Type(), "")) {
-            return false;
-        }
-
-        ScopedParen sp(out);
-
-        for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) {
-            if (column_idx > 0) {
-                out << ", ";
-            }
-            size_t start = m->rows() * column_idx;
-            size_t end = m->rows() * (column_idx + 1);
-            if (!emit_vector(m->ColumnType(), start, end)) {
+        },
+        [&](const sem::Matrix* m) {
+            if (!EmitType(out, m, "")) {
                 return false;
             }
-        }
-        return true;
-    };
-    return Switch(
-        constant.Type(),                                                                   //
-        [&](const sem::Bool*) { return emit_bool(0); },                                    //
-        [&](const sem::F32*) { return emit_f32(0); },                                      //
-        [&](const sem::I32*) { return emit_i32(0); },                                      //
-        [&](const sem::U32*) { return emit_u32(0); },                                      //
-        [&](const sem::Vector* v) { return emit_vector(v, 0, constant.ElementCount()); },  //
-        [&](const sem::Matrix* m) { return emit_matrix(m); },                              //
+
+            ScopedParen sp(out);
+
+            for (size_t i = 0; i < m->columns(); i++) {
+                if (i > 0) {
+                    out << ", ";
+                }
+                if (!EmitConstant(out, constant->Index(i))) {
+                    return false;
+                }
+            }
+            return true;
+        },
+        [&](const sem::Array* a) {
+            if (!EmitType(out, a, "")) {
+                return false;
+            }
+
+            if (constant->AllZero()) {
+                out << "{}";
+                return true;
+            }
+
+            out << "{";
+            TINT_DEFER(out << "}");
+
+            for (size_t i = 0; i < a->Count(); i++) {
+                if (i > 0) {
+                    out << ", ";
+                }
+                if (!EmitConstant(out, constant->Index(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
         [&](Default) {
             diagnostics_.add_error(
                 diag::System::Writer,
-                "unhandled constant type: " + builder_.FriendlyName(constant.Type()));
+                "unhandled constant type: " + builder_.FriendlyName(constant->Type()));
             return false;
         });
 }
@@ -1698,8 +1720,14 @@
 
 bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) {
     if (auto* sem = builder_.Sem().Get(expr)) {
-        if (auto constant = sem->ConstantValue()) {
-            return EmitConstant(out, constant);
+        if (auto* user = sem->As<sem::VariableUser>();
+            !user || !user->Variable()->Declaration()->Is<ast::Let>()) {
+            // Disable constant inlining if the constant expression is from a 'let' declaration.
+            // TODO(crbug.com/tint/1580): Once 'const' is implemented, 'let' will no longer resolve
+            // to a shader-creation time constant value, and this can be removed.
+            if (auto constant = sem->ConstantValue()) {
+                return EmitConstant(out, constant);
+            }
         }
     }
     return Switch(
@@ -1775,8 +1803,8 @@
             if (!EmitType(out, type, param_name)) {
                 return false;
             }
-            // Parameter name is output as part of the type for arrays and pointers.
-            if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+            // Parameter name is output as part of the type for pointers.
+            if (!type->Is<sem::Pointer>()) {
                 out << " " << program_->Symbols().NameFor(v->symbol);
             }
         }
@@ -1899,8 +1927,8 @@
             if (!EmitType(out, type, param_name)) {
                 return false;
             }
-            // Parameter name is output as part of the type for arrays and pointers.
-            if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+            // Parameter name is output as part of the type for pointers.
+            if (!type->Is<sem::Pointer>()) {
                 out << " " << param_name;
             }
 
@@ -2341,6 +2369,9 @@
                 v->variable,  //
                 [&](const ast::Var* var) { return EmitVar(var); },
                 [&](const ast::Let* let) { return EmitLet(let); },
+                [&](const ast::Const*) {
+                    return true;  // Constants are embedded at their use
+                },
                 [&](Default) {  //
                     TINT_ICE(Writer, diagnostics_)
                         << "unknown statement type: " << stmt->TypeInfo().name;
@@ -2415,29 +2446,12 @@
                 << "unhandled atomic type " << atomic->Type()->FriendlyName(builder_.Symbols());
             return false;
         },
-        [&](const sem::Array* ary) {
-            const sem::Type* base_type = ary;
-            std::vector<uint32_t> sizes;
-            while (auto* arr = base_type->As<sem::Array>()) {
-                if (arr->IsRuntimeSized()) {
-                    sizes.push_back(1);
-                } else {
-                    sizes.push_back(arr->Count());
-                }
-                base_type = arr->ElemType();
-            }
-            if (!EmitType(out, base_type, "")) {
+        [&](const sem::Array* arr) {
+            out << ArrayType() << "<";
+            if (!EmitType(out, arr->ElemType(), "")) {
                 return false;
             }
-            if (!name.empty()) {
-                out << " " << name;
-                if (name_printed) {
-                    *name_printed = true;
-                }
-            }
-            for (uint32_t size : sizes) {
-                out << "[" << size << "]";
-            }
+            out << ", " << (arr->IsRuntimeSized() ? 1u : arr->Count()) << ">";
             return true;
         },
         [&](const sem::Bool*) {
@@ -2472,22 +2486,12 @@
                 return false;
             }
             out << " ";
-            if (ptr->StoreType()->Is<sem::Array>()) {
-                std::string inner = "(*" + name + ")";
-                if (!EmitType(out, ptr->StoreType(), inner)) {
-                    return false;
-                }
-                if (name_printed) {
-                    *name_printed = true;
-                }
-            } else {
-                if (!EmitType(out, ptr->StoreType(), "")) {
-                    return false;
-                }
-                out << "* " << name;
-                if (name_printed) {
-                    *name_printed = true;
-                }
+            if (!EmitType(out, ptr->StoreType(), "")) {
+                return false;
+            }
+            out << "* " << name;
+            if (name_printed) {
+                *name_printed = true;
             }
             return true;
         },
@@ -2703,7 +2707,7 @@
 
         auto out = line(b);
         add_byte_offset_comment(out, msl_offset);
-        out << "int8_t " << name << "[" << size << "];";
+        out << ArrayType() << "<int8_t, " << size << "> " << name << ";";
     };
 
     b->IncrementIndent();
@@ -2741,11 +2745,7 @@
 
         auto* ty = mem->Type();
 
-        // Array member name will be output with the type
-        if (!ty->Is<sem::Array>()) {
-            out << " " << mem_name;
-        }
-
+        out << " " << mem_name;
         // Emit attributes
         if (auto* decl = mem->Declaration()) {
             for (auto* attr : decl->attributes) {
@@ -2948,8 +2948,8 @@
     if (!EmitType(out, type, name)) {
         return false;
     }
-    // Variable name is output as part of the type for arrays and pointers.
-    if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+    // Variable name is output as part of the type for pointers.
+    if (!type->Is<sem::Pointer>()) {
         out << " " << name;
     }
 
@@ -2998,8 +2998,8 @@
         return false;
     }
 
-    // Variable name is output as part of the type for arrays and pointers.
-    if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+    // Variable name is output as part of the type for pointers.
+    if (!type->Is<sem::Pointer>()) {
         out << " " << name;
     }
 
@@ -3012,30 +3012,6 @@
     return true;
 }
 
-bool GeneratorImpl::EmitProgramConstVariable(const ast::Let* let) {
-    auto* global = program_->Sem().Get<sem::GlobalVariable>(let);
-    auto* type = global->Type();
-
-    auto out = line();
-    out << "constant ";
-    if (!EmitType(out, type, program_->Symbols().NameFor(let->symbol))) {
-        return false;
-    }
-    if (!type->Is<sem::Array>()) {
-        out << " " << program_->Symbols().NameFor(let->symbol);
-    }
-
-    if (let->constructor != nullptr) {
-        out << " = ";
-        if (!EmitExpression(out, let->constructor)) {
-            return false;
-        }
-    }
-    out << ";";
-
-    return true;
-}
-
 bool GeneratorImpl::EmitOverride(const ast::Override* override) {
     auto* global = program_->Sem().Get<sem::GlobalVariable>(override);
     auto* type = global->Type();
@@ -3045,9 +3021,7 @@
     if (!EmitType(out, type, program_->Symbols().NameFor(override->symbol))) {
         return false;
     }
-    if (!type->Is<sem::Array>()) {
-        out << " " << program_->Symbols().NameFor(override->symbol);
-    }
+    out << " " << program_->Symbols().NameFor(override->symbol);
 
     out << " [[function_constant(" << global->ConstantId() << ")]];";
 
@@ -3120,8 +3094,8 @@
 
         [&](const sem::Array* arr) {
             if (!arr->IsStrideImplicit()) {
-                TINT_ICE(Writer, diagnostics_) << "arrays with explicit strides not "
-                                                  "exist past the SPIR-V reader";
+                TINT_ICE(Writer, diagnostics_)
+                    << "arrays with explicit strides should not exist past the SPIR-V reader";
                 return SizeAndAlign{};
             }
             auto num_els = std::max<uint32_t>(arr->Count(), 1);
@@ -3208,4 +3182,25 @@
     return true;
 }
 
+const std::string& GeneratorImpl::ArrayType() {
+    if (array_template_name_.empty()) {
+        array_template_name_ = UniqueIdentifier("tint_array");
+        auto* buf = &helpers_;
+        line(buf) << "template<typename T, size_t N>";
+        line(buf) << "struct " << array_template_name_ << " {";
+        line(buf) << "    const constant T& operator[](size_t i) const constant"
+                  << " { return elements[i]; }";
+        for (auto* space : {"device", "thread", "threadgroup"}) {
+            line(buf) << "    " << space << " T& operator[](size_t i) " << space
+                      << " { return elements[i]; }";
+            line(buf) << "    const " << space << " T& operator[](size_t i) const " << space
+                      << " { return elements[i]; }";
+        }
+        line(buf) << "    T elements[N];";
+        line(buf) << "};";
+        line(buf);
+    }
+    return array_template_name_;
+}
+
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/msl/generator_impl.h b/src/tint/writer/msl/generator_impl.h
index 59af5c3..f27d258 100644
--- a/src/tint/writer/msl/generator_impl.h
+++ b/src/tint/writer/msl/generator_impl.h
@@ -256,7 +256,7 @@
     /// @param out the output stream
     /// @param constant the constant value to emit
     /// @returns true if the constant value was successfully emitted
-    bool EmitConstant(std::ostream& out, const sem::Constant& constant);
+    bool EmitConstant(std::ostream& out, const sem::Constant* constant);
     /// Handles a literal
     /// @param out the output of the expression stream
     /// @param lit the literal to emit
@@ -356,10 +356,6 @@
     /// @param let the variable to generate
     /// @returns true if the variable was emitted
     bool EmitLet(const ast::Let* let);
-    /// Handles generating a module-scope 'let' declaration
-    /// @param let the 'let' to emit
-    /// @returns true if the variable was emitted
-    bool EmitProgramConstVariable(const ast::Let* let);
     /// Handles generating a module-scope 'override' declaration
     /// @param override the 'override' to emit
     /// @returns true if the variable was emitted
@@ -413,6 +409,10 @@
                            const sem::Builtin* builtin,
                            F&& build);
 
+    /// @returns the name of the templated tint_array helper type, generating it if this is the
+    /// first call.
+    const std::string& ArrayType();
+
     TextBuffer helpers_;  // Helper functions emitted at the top of the output
 
     /// @returns the MSL packed type size and alignment in bytes for the given
@@ -427,13 +427,17 @@
         utils::UnorderedKeyWrapper<std::tuple<ast::StorageClass, const sem::Struct*>>;
     std::unordered_map<ACEWKeyType, std::string> atomicCompareExchangeWeak_;
 
-    /// Unique name of the 'TINT_INVARIANT' preprocessor define. Non-empty only if
-    /// an invariant attribute has been generated.
+    /// Unique name of the 'TINT_INVARIANT' preprocessor define.
+    /// Non-empty only if an invariant attribute has been generated.
     std::string invariant_define_name_;
 
     /// True if matrix-packed_vector operator overloads have been generated.
     bool matrix_packed_vector_overloads_ = false;
 
+    /// Unique name of the tint_array<T, N> template.
+    /// Non-empty only if the template has been generated.
+    std::string array_template_name_;
+
     /// A map from entry point name to a list of dynamic workgroup allocations.
     /// Each entry in the vector is the size of the workgroup allocation that
     /// should be created for that index.
diff --git a/src/tint/writer/msl/generator_impl_array_accessor_test.cc b/src/tint/writer/msl/generator_impl_array_accessor_test.cc
index 7475b67..bfa76dc 100644
--- a/src/tint/writer/msl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/msl/generator_impl_array_accessor_test.cc
@@ -34,7 +34,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, IndexAccessor_OfDref) {
-    Global("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
 
     auto* p = Let("p", nullptr, AddressOf("ary"));
     auto* expr = IndexAccessor(Deref("p"), 5_i);
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index bfe5548..9f00be7 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -171,15 +171,15 @@
 TEST_P(MslBuiltinTest, Emit) {
     auto param = GetParam();
 
-    Global("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    Global("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-    Global("f4", ty.vec4<f32>(), ast::StorageClass::kPrivate);
-    Global("u1", ty.u32(), ast::StorageClass::kPrivate);
-    Global("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
-    Global("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
-    Global("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
-    Global("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
-    Global("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("f4", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("u1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("u2", ty.vec2<u32>(), ast::StorageClass::kPrivate);
+    GlobalVar("i2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("b2", ty.vec2<bool>(), ast::StorageClass::kPrivate);
+    GlobalVar("m2x2", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("m3x2", ty.mat3x2<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = GenerateCall(param.builtin, param.type, this);
     ASSERT_NE(nullptr, call) << "Unhandled builtin";
@@ -273,8 +273,8 @@
         BuiltinData{BuiltinType::kUnpack2x16unorm, ParamType::kU32, "unpack_unorm2x16_to_float"}));
 
 TEST_F(MslGeneratorImplTest, Builtin_Call) {
-    Global("param1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-    Global("param2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
 
     auto* call = Call("dot", "param1", "param2");
     WrapInFunction(CallStmt(call));
@@ -410,7 +410,7 @@
 
 TEST_F(MslGeneratorImplTest, Pack2x16Float) {
     auto* call = Call("pack2x16float", "p1");
-    Global("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.vec2<f32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
 
     GeneratorImpl& gen = Build();
@@ -422,7 +422,7 @@
 
 TEST_F(MslGeneratorImplTest, Unpack2x16Float) {
     auto* call = Call("unpack2x16float", "p1");
-    Global("p1", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("p1", ty.u32(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(call));
 
     GeneratorImpl& gen = Build();
@@ -433,7 +433,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, DotI32) {
-    Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
     WrapInFunction(CallStmt(Call("dot", "v", "v")));
 
     GeneratorImpl& gen = SanitizeAndBuild();
diff --git a/src/tint/writer/msl/generator_impl_call_test.cc b/src/tint/writer/msl/generator_impl_call_test.cc
index 93c9b83..c7f3a02 100644
--- a/src/tint/writer/msl/generator_impl_call_test.cc
+++ b/src/tint/writer/msl/generator_impl_call_test.cc
@@ -42,8 +42,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.f32(), {Return(1.23_f)});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -62,8 +62,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     auto* stmt = CallStmt(call);
diff --git a/src/tint/writer/msl/generator_impl_function_test.cc b/src/tint/writer/msl/generator_impl_function_test.cc
index f7d2779..e520d7a 100644
--- a/src/tint/writer/msl/generator_impl_function_test.cc
+++ b/src/tint/writer/msl/generator_impl_function_test.cc
@@ -335,11 +335,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
@@ -377,11 +377,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("coord", "b"));
 
@@ -415,11 +415,11 @@
 
 TEST_F(MslGeneratorImplTest, Emit_Attribute_Called_By_EntryPoint_With_Uniform) {
     auto* ubo_ty = Structure("UBO", {Member("coord", ty.vec4<f32>())});
-    auto* ubo = Global("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(0),
-                       });
+    auto* ubo = GlobalVar("ubo", ty.Of(ubo_ty), ast::StorageClass::kUniform,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(0u),
+                          });
 
     Func("sub_func",
          {
@@ -469,11 +469,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     Func("sub_func",
          {
@@ -524,11 +524,11 @@
                                     Member("b", ty.f32()),
                                 });
 
-    Global("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("coord", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     Func("sub_func",
          {
@@ -591,11 +591,20 @@
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
-  struct tint_array_wrapper {
-    float arr[5];
-  };
 
-  void my_func(tint_array_wrapper a) {
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+  void my_func(tint_array<float, 5> a) {
     return;
   }
 
@@ -616,12 +625,21 @@
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
-  struct tint_array_wrapper {
-    float arr[5];
-  };
 
-  tint_array_wrapper my_func() {
-    tint_array_wrapper const tint_symbol = {.arr={}};
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+  tint_array<float, 5> my_func() {
+    tint_array<float, 5> const tint_symbol = tint_array<float, 5>{};
     return tint_symbol;
   }
 
@@ -647,11 +665,11 @@
 
     auto* s = Structure("Data", {Member("d", ty.f32())});
 
-    Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
diff --git a/src/tint/writer/msl/generator_impl_import_test.cc b/src/tint/writer/msl/generator_impl_import_test.cc
index de9353e..7b79569 100644
--- a/src/tint/writer/msl/generator_impl_import_test.cc
+++ b/src/tint/writer/msl/generator_impl_import_test.cc
@@ -234,7 +234,7 @@
                                          MslImportData{"clamp", "clamp"}));
 
 TEST_F(MslGeneratorImplTest, MslImportData_Determinant) {
-    Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = Call("determinant", "var");
 
diff --git a/src/tint/writer/msl/generator_impl_loop_test.cc b/src/tint/writer/msl/generator_impl_loop_test.cc
index 85f1deb..50b69d0 100644
--- a/src/tint/writer/msl/generator_impl_loop_test.cc
+++ b/src/tint/writer/msl/generator_impl_loop_test.cc
@@ -64,8 +64,8 @@
 TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
     Func("a_statement", {}, ty.void_(), {});
 
-    Global("lhs", ty.f32(), ast::StorageClass::kPrivate);
-    Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("lhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* body = Block(create<ast::DiscardStatement>());
     auto* continuing = Block(CallStmt(Call("a_statement")));
@@ -107,7 +107,7 @@
     // }
     //
 
-    Global("rhs", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("rhs", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* body = Block(Decl(Var("lhs", ty.f32(), Expr(2.4_f))),  //
                        Decl(Var("other", ty.f32())),             //
@@ -184,7 +184,7 @@
     Func("f", {Param("i", ty.i32())}, ty.void_(), {});
     auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
 
-    Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
     auto* multi_stmt = Block(f(1_i), f(2_i));
     auto* loop = For(multi_stmt, nullptr, nullptr,  //
                      Block(Return()));
@@ -260,7 +260,7 @@
     Func("f", {Param("i", ty.i32())}, ty.void_(), {});
     auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
 
-    Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
     auto* multi_stmt = Block(f(1_i), f(2_i));
     auto* loop = For(nullptr, nullptr, multi_stmt,  //
                      Block(Return()));
@@ -315,7 +315,7 @@
     Func("f", {Param("i", ty.i32())}, ty.void_(), {});
     auto f = [&](auto&& expr) { return CallStmt(Call("f", expr)); };
 
-    Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
     auto* multi_stmt_a = Block(f(1_i), f(2_i));
     auto* multi_stmt_b = Block(f(3_i), f(4_i));
     auto* loop = For(multi_stmt_a, Expr(true), multi_stmt_b,  //
diff --git a/src/tint/writer/msl/generator_impl_member_accessor_test.cc b/src/tint/writer/msl/generator_impl_member_accessor_test.cc
index c9f3da0..96824a1 100644
--- a/src/tint/writer/msl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/msl/generator_impl_member_accessor_test.cc
@@ -20,8 +20,8 @@
 using MslGeneratorImplTest = TestHelper;
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor) {
-    Global("str", ty.Of(Structure("my_str", {Member("mem", ty.f32())})),
-           ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(Structure("my_str", {Member("mem", ty.f32())})),
+              ast::StorageClass::kPrivate);
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(expr);
 
@@ -33,7 +33,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor_Swizzle_xyz) {
-    Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = MemberAccessor("my_vec", "xyz");
     WrapInFunction(expr);
@@ -45,7 +45,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, EmitExpression_MemberAccessor_Swizzle_gbr) {
-    Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
 
     auto* expr = MemberAccessor("my_vec", "gbr");
     WrapInFunction(expr);
diff --git a/src/tint/writer/msl/generator_impl_module_constant_test.cc b/src/tint/writer/msl/generator_impl_module_constant_test.cc
index f23e51a..c204686 100644
--- a/src/tint/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/msl/generator_impl_module_constant_test.cc
@@ -22,16 +22,254 @@
 
 using MslGeneratorImplTest = TestHelper;
 
-TEST_F(MslGeneratorImplTest, Emit_ModuleConstant) {
-    auto* var = GlobalConst("pos", ty.array<f32, 3>(), array<f32, 3>(1_f, 2_f, 3_f));
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_AInt) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
 
     GeneratorImpl& gen = Build();
 
-    ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.error();
-    EXPECT_EQ(gen.result(), "constant float pos[3] = {1.0f, 2.0f, 3.0f};\n");
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
 }
 
-TEST_F(MslGeneratorImplTest, Emit_SpecConstant) {
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_i32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_u32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  uint const l = 1u;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_f32) {
+    auto* var = GlobalConst("G", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_AInt) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int3 const l = int3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_AFloat) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_f32) {
+    auto* var = GlobalConst("G", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_AFloat) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_f32) {
+    auto* var = GlobalConst("G", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_arr_f32) {
+    auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<float, 3> const l = tint_array<float, 3>{1.0f, 2.0f, 3.0f};
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_arr_vec2_bool) {
+    auto* var = GlobalConst("G", nullptr,
+                            Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                                      vec2<bool>(true, false),         //
+                                      vec2<bool>(false, true),         //
+                                      vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<bool2, 3> const l = tint_array<bool2, 3>{bool2(true, false), bool2(false, true), bool2(true)};
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_Override) {
     auto* var = Override("pos", ty.f32(), Expr(3_f),
                          ast::AttributeList{
                              Id(23),
@@ -43,7 +281,7 @@
     EXPECT_EQ(gen.result(), "constant float pos [[function_constant(23)]];\n");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_SpecConstant_NoId) {
+TEST_F(MslGeneratorImplTest, Emit_Override_NoId) {
     auto* var_a = Override("a", ty.f32(), nullptr,
                            ast::AttributeList{
                                Id(0),
diff --git a/src/tint/writer/msl/generator_impl_sanitizer_test.cc b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
index 475025c..1514373 100644
--- a/src/tint/writer/msl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
@@ -27,11 +27,11 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -50,12 +50,25 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct my_struct {
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(30)]]) {
@@ -72,11 +85,11 @@
                                          Member(0, "z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -95,13 +108,26 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct my_struct {
   float z;
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(30)]]) {
@@ -116,11 +142,11 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
@@ -143,12 +169,25 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct my_struct {
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(30)]]) {
@@ -163,16 +202,16 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniform) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(0),
-           });
-    Global("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(2),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(0u),
+              });
+    GlobalVar("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(2u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
@@ -196,12 +235,25 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[2];
+  /* 0x0000 */ tint_array<uint4, 2> buffer_size;
 };
 
 struct my_struct {
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(29)]]) {
@@ -215,16 +267,16 @@
 
 TEST_F(MslSanitizerTest, Call_ArrayLength_ArrayLengthFromUniformMissingBinding) {
     auto* s = Structure("my_struct", {Member(0, "a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(0),
-           });
-    Global("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(2),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(0u),
+              });
+    GlobalVar("c", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(2u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     Func("a_func", {}, ty.void_(),
          {
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index 183fef5..aec9bfb 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -141,7 +141,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrix) {
-    Global("m", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
     Func("comp_main", {}, ty.void_(), {Decl(Let("x", nullptr, Expr("m")))},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
@@ -178,7 +178,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrixInArray) {
-    Global("m", ty.array(ty.mat2x2<f32>(), 4_i), ast::StorageClass::kWorkgroup);
+    GlobalVar("m", ty.array(ty.mat2x2<f32>(), 4_i), ast::StorageClass::kWorkgroup);
     Func("comp_main", {}, ty.void_(), {Decl(Let("x", nullptr, Expr("m")))},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
@@ -188,25 +188,34 @@
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float2x2 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct tint_symbol_3 {
-  tint_array_wrapper m;
+  tint_array<float2x2, 4> m;
 };
 
-void comp_main_inner(uint local_invocation_index, threadgroup tint_array_wrapper* const tint_symbol) {
+void comp_main_inner(uint local_invocation_index, threadgroup tint_array<float2x2, 4>* const tint_symbol) {
   for(uint idx = local_invocation_index; (idx < 4u); idx = (idx + 1u)) {
     uint const i = idx;
-    (*(tint_symbol)).arr[i] = float2x2(float2(0.0f), float2(0.0f));
+    (*(tint_symbol))[i] = float2x2(float2(0.0f), float2(0.0f));
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
-  tint_array_wrapper const x = *(tint_symbol);
+  tint_array<float2x2, 4> const x = *(tint_symbol);
 }
 
 kernel void comp_main(threadgroup tint_symbol_3* tint_symbol_2 [[threadgroup(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array_wrapper* const tint_symbol_1 = &((*(tint_symbol_2)).m);
+  threadgroup tint_array<float2x2, 4>* const tint_symbol_1 = &((*(tint_symbol_2)).m);
   comp_main_inner(local_invocation_index, tint_symbol_1);
   return;
 }
@@ -227,7 +236,7 @@
     Structure("S2", {
                         Member("s", ty.type_name("S1")),
                     });
-    Global("s", ty.type_name("S2"), ast::StorageClass::kWorkgroup);
+    GlobalVar("s", ty.type_name("S2"), ast::StorageClass::kWorkgroup);
     Func("comp_main", {}, ty.void_(), {Decl(Let("x", nullptr, Expr("s")))},
          {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1_i)});
 
@@ -274,15 +283,15 @@
 }
 
 TEST_F(MslGeneratorImplTest, WorkgroupMatrix_Multiples) {
-    Global("m1", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m2", ty.mat2x3<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m3", ty.mat2x4<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m4", ty.mat3x2<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m5", ty.mat3x3<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m6", ty.mat3x4<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m7", ty.mat4x2<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m8", ty.mat4x3<f32>(), ast::StorageClass::kWorkgroup);
-    Global("m9", ty.mat4x4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m1", ty.mat2x2<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m2", ty.mat2x3<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m3", ty.mat2x4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m4", ty.mat3x2<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m5", ty.mat3x3<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m6", ty.mat3x4<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m7", ty.mat4x2<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m8", ty.mat4x3<f32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("m9", ty.mat4x4<f32>(), ast::StorageClass::kWorkgroup);
     Func("main1", {}, ty.void_(),
          {
              Decl(Let("a1", nullptr, Expr("m1"))),
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index 257468b..070b1bb 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -31,6 +31,20 @@
 namespace tint::writer::msl {
 namespace {
 
+void FormatMSLField(std::stringstream& out,
+                    const char* addr,
+                    const char* type,
+                    size_t array_count,
+                    const char* name) {
+    out << "  /* " << std::string(addr) << " */ ";
+    if (array_count == 0) {
+        out << type << " ";
+    } else {
+        out << "tint_array<" << type << ", " << std::to_string(array_count) << "> ";
+    }
+    out << name << ";\n";
+}
+
 #define CHECK_TYPE_SIZE_AND_ALIGN(TYPE, SIZE, ALIGN)      \
     static_assert(sizeof(TYPE) == SIZE, "Bad type size"); \
     static_assert(alignof(TYPE) == ALIGN, "Bad type alignment")
@@ -63,60 +77,60 @@
 
 TEST_F(MslGeneratorImplTest, EmitType_Array) {
     auto* arr = ty.array<bool, 4>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[4]");
+    EXPECT_EQ(out.str(), "tint_array<bool, 4>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) {
     auto* a = ty.array<bool, 4>();
     auto* b = ty.array(a, 5_u);
-    Global("G", b, ast::StorageClass::kPrivate);
+    GlobalVar("G", b, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(b), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[5][4]");
+    EXPECT_EQ(out.str(), "tint_array<tint_array<bool, 4>, 5>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArrayOfArray) {
     auto* a = ty.array<bool, 4>();
     auto* b = ty.array(a, 5_u);
     auto* c = ty.array(b, 6_u);
-    Global("G", c, ast::StorageClass::kPrivate);
+    GlobalVar("G", c, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(c), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[6][5][4]");
+    EXPECT_EQ(out.str(), "tint_array<tint_array<tint_array<bool, 4>, 5>, 6>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) {
     auto* arr = ty.array<bool, 4>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "")) << gen.error();
-    EXPECT_EQ(out.str(), "bool[4]");
+    EXPECT_EQ(out.str(), "tint_array<bool, 4>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) {
     auto* arr = ty.array<bool, 1>();
-    Global("G", arr, ast::StorageClass::kPrivate);
+    GlobalVar("G", arr, ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[1]");
+    EXPECT_EQ(out.str(), "tint_array<bool, 1>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Bool) {
@@ -233,11 +247,11 @@
                                  Member("z", ty.f32()),
                              });
 
-    Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -245,54 +259,58 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                               \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)           \
-    FIELD(0x0004, int8_t, tint_pad, [124])         \
-    FIELD(0x0080, float, b, /*NO SUFFIX*/)         \
-    FIELD(0x0084, int8_t, tint_pad_1, [124])       \
-    FIELD(0x0100, float2, c, /*NO SUFFIX*/)        \
-    FIELD(0x0108, uint, d, /*NO SUFFIX*/)          \
-    FIELD(0x010c, int8_t, tint_pad_2, [4])         \
-    FIELD(0x0110, packed_float3, e, /*NO SUFFIX*/) \
-    FIELD(0x011c, uint, f, /*NO SUFFIX*/)          \
-    FIELD(0x0120, float4, g, /*NO SUFFIX*/)        \
-    FIELD(0x0130, uint, h, /*NO SUFFIX*/)          \
-    FIELD(0x0134, int8_t, tint_pad_3, [4])         \
-    FIELD(0x0138, float2x2, i, /*NO SUFFIX*/)      \
-    FIELD(0x0148, uint, j, /*NO SUFFIX*/)          \
-    FIELD(0x014c, int8_t, tint_pad_4, [4])         \
-    FIELD(0x0150, float2x3, k, /*NO SUFFIX*/)      \
-    FIELD(0x0170, uint, l, /*NO SUFFIX*/)          \
-    FIELD(0x0174, int8_t, tint_pad_5, [12])        \
-    FIELD(0x0180, float2x4, m, /*NO SUFFIX*/)      \
-    FIELD(0x01a0, uint, n, /*NO SUFFIX*/)          \
-    FIELD(0x01a4, int8_t, tint_pad_6, [4])         \
-    FIELD(0x01a8, float3x2, o, /*NO SUFFIX*/)      \
-    FIELD(0x01c0, uint, p, /*NO SUFFIX*/)          \
-    FIELD(0x01c4, int8_t, tint_pad_7, [12])        \
-    FIELD(0x01d0, float3x3, q, /*NO SUFFIX*/)      \
-    FIELD(0x0200, uint, r, /*NO SUFFIX*/)          \
-    FIELD(0x0204, int8_t, tint_pad_8, [12])        \
-    FIELD(0x0210, float3x4, s, /*NO SUFFIX*/)      \
-    FIELD(0x0240, uint, t, /*NO SUFFIX*/)          \
-    FIELD(0x0244, int8_t, tint_pad_9, [4])         \
-    FIELD(0x0248, float4x2, u, /*NO SUFFIX*/)      \
-    FIELD(0x0268, uint, v, /*NO SUFFIX*/)          \
-    FIELD(0x026c, int8_t, tint_pad_10, [4])        \
-    FIELD(0x0270, float4x3, w, /*NO SUFFIX*/)      \
-    FIELD(0x02b0, uint, x, /*NO SUFFIX*/)          \
-    FIELD(0x02b4, int8_t, tint_pad_11, [12])       \
-    FIELD(0x02c0, float4x4, y, /*NO SUFFIX*/)      \
-    FIELD(0x0300, float, z, /*NO SUFFIX*/)         \
-    FIELD(0x0304, int8_t, tint_pad_12, [124])
+#define ALL_FIELDS()                       \
+    FIELD(0x0000, int, 0, a)               \
+    FIELD(0x0004, int8_t, 124, tint_pad)   \
+    FIELD(0x0080, float, 0, b)             \
+    FIELD(0x0084, int8_t, 124, tint_pad_1) \
+    FIELD(0x0100, float2, 0, c)            \
+    FIELD(0x0108, uint, 0, d)              \
+    FIELD(0x010c, int8_t, 4, tint_pad_2)   \
+    FIELD(0x0110, packed_float3, 0, e)     \
+    FIELD(0x011c, uint, 0, f)              \
+    FIELD(0x0120, float4, 0, g)            \
+    FIELD(0x0130, uint, 0, h)              \
+    FIELD(0x0134, int8_t, 4, tint_pad_3)   \
+    FIELD(0x0138, float2x2, 0, i)          \
+    FIELD(0x0148, uint, 0, j)              \
+    FIELD(0x014c, int8_t, 4, tint_pad_4)   \
+    FIELD(0x0150, float2x3, 0, k)          \
+    FIELD(0x0170, uint, 0, l)              \
+    FIELD(0x0174, int8_t, 12, tint_pad_5)  \
+    FIELD(0x0180, float2x4, 0, m)          \
+    FIELD(0x01a0, uint, 0, n)              \
+    FIELD(0x01a4, int8_t, 4, tint_pad_6)   \
+    FIELD(0x01a8, float3x2, 0, o)          \
+    FIELD(0x01c0, uint, 0, p)              \
+    FIELD(0x01c4, int8_t, 12, tint_pad_7)  \
+    FIELD(0x01d0, float3x3, 0, q)          \
+    FIELD(0x0200, uint, 0, r)              \
+    FIELD(0x0204, int8_t, 12, tint_pad_8)  \
+    FIELD(0x0210, float3x4, 0, s)          \
+    FIELD(0x0240, uint, 0, t)              \
+    FIELD(0x0244, int8_t, 4, tint_pad_9)   \
+    FIELD(0x0248, float4x2, 0, u)          \
+    FIELD(0x0268, uint, 0, v)              \
+    FIELD(0x026c, int8_t, 4, tint_pad_10)  \
+    FIELD(0x0270, float4x3, 0, w)          \
+    FIELD(0x02b0, uint, 0, x)              \
+    FIELD(0x02b4, int8_t, 12, tint_pad_11) \
+    FIELD(0x02c0, float4x4, 0, y)          \
+    FIELD(0x0300, float, 0, z)             \
+    FIELD(0x0304, int8_t, 124, tint_pad_12)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 
     // 1.4 Metal and C++14
     // The Metal programming language is a C++14-based Specification with
@@ -304,12 +322,12 @@
     // layout is as expected for C++14 / MSL.
     {
         struct S {
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
             ALL_FIELDS()
 #undef FIELD
         };
 
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
     EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
         ALL_FIELDS()
 #undef FIELD
@@ -338,11 +356,11 @@
                                  Member("e", ty.f32()),
                              });
 
-    Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -350,22 +368,26 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                         \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)     \
-    FIELD(0x0004, int8_t, tint_pad, [508])   \
-    FIELD(0x0200, inner_x, b, /*NO SUFFIX*/) \
-    FIELD(0x0600, float, c, /*NO SUFFIX*/)   \
-    FIELD(0x0604, inner_y, d, /*NO SUFFIX*/) \
-    FIELD(0x0808, float, e, /*NO SUFFIX*/)   \
-    FIELD(0x080c, int8_t, tint_pad_1, [500])
+#define ALL_FIELDS()                     \
+    FIELD(0x0000, int, 0, a)             \
+    FIELD(0x0004, int8_t, 508, tint_pad) \
+    FIELD(0x0200, inner_x, 0, b)         \
+    FIELD(0x0600, float, 0, c)           \
+    FIELD(0x0604, inner_y, 0, d)         \
+    FIELD(0x0808, float, 0, e)           \
+    FIELD(0x080c, int8_t, 500, tint_pad_1)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 
     // 1.4 Metal and C++14
     // The Metal programming language is a C++14-based Specification with
@@ -389,12 +411,12 @@
         CHECK_TYPE_SIZE_AND_ALIGN(inner_y, 516, 4);
 
         struct S {
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
             ALL_FIELDS()
 #undef FIELD
         };
 
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
     EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
         ALL_FIELDS()
 #undef FIELD
@@ -428,11 +450,11 @@
                                  Member("f", array_z),
                              });
 
-    Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -440,23 +462,27 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                       \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)   \
-    FIELD(0x0004, float, b, [7])           \
-    FIELD(0x0020, float, c, /*NO SUFFIX*/) \
-    FIELD(0x0024, int8_t, tint_pad, [476]) \
-    FIELD(0x0200, inner, d, [4])           \
-    FIELD(0x1200, float, e, /*NO SUFFIX*/) \
-    FIELD(0x1204, float, f, [1])           \
-    FIELD(0x1208, int8_t, tint_pad_1, [504])
+#define ALL_FIELDS()                     \
+    FIELD(0x0000, int, 0, a)             \
+    FIELD(0x0004, float, 7, b)           \
+    FIELD(0x0020, float, 0, c)           \
+    FIELD(0x0024, int8_t, 476, tint_pad) \
+    FIELD(0x0200, inner, 4, d)           \
+    FIELD(0x1200, float, 0, e)           \
+    FIELD(0x1204, float, 1, f)           \
+    FIELD(0x1208, int8_t, 504, tint_pad_1)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 
     // 1.4 Metal and C++14
     // The Metal programming language is a C++14-based Specification with
@@ -486,12 +512,12 @@
         CHECK_TYPE_SIZE_AND_ALIGN(array_z, 4, 4);
 
         struct S {
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
             ALL_FIELDS()
 #undef FIELD
         };
 
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
     EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
         ALL_FIELDS()
 #undef FIELD
@@ -510,11 +536,11 @@
                                  Member("c", ty.i32()),
                              });
 
-    Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -522,20 +548,24 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                      \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)  \
-    FIELD(0x0004, int8_t, tint_pad, [12]) \
-    FIELD(0x0010, float3, b, [4])         \
-    FIELD(0x0050, int, c, /*NO SUFFIX*/)  \
-    FIELD(0x0054, int8_t, tint_pad_1, [12])
+#define ALL_FIELDS()                    \
+    FIELD(0x0000, int, 0, a)            \
+    FIELD(0x0004, int8_t, 12, tint_pad) \
+    FIELD(0x0010, float3, 4, b)         \
+    FIELD(0x0050, int, 0, c)            \
+    FIELD(0x0054, int8_t, 12, tint_pad_1)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 }
 
 TEST_F(MslGeneratorImplTest, AttemptTintPadSymbolCollision) {
@@ -570,11 +600,11 @@
                            Member("tint_pad_21", ty.f32()),
                        });
 
-    Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -583,44 +613,44 @@
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
     EXPECT_EQ(buf.String(), R"(struct S {
   /* 0x0000 */ int tint_pad_2;
-  /* 0x0004 */ int8_t tint_pad_10[124];
+  /* 0x0004 */ tint_array<int8_t, 124> tint_pad_10;
   /* 0x0080 */ float tint_pad_20;
-  /* 0x0084 */ int8_t tint_pad_11[124];
+  /* 0x0084 */ tint_array<int8_t, 124> tint_pad_11;
   /* 0x0100 */ float2 tint_pad_33;
   /* 0x0108 */ uint tint_pad_1;
-  /* 0x010c */ int8_t tint_pad_12[4];
+  /* 0x010c */ tint_array<int8_t, 4> tint_pad_12;
   /* 0x0110 */ packed_float3 tint_pad_3;
   /* 0x011c */ uint tint_pad_7;
   /* 0x0120 */ float4 tint_pad_25;
   /* 0x0130 */ uint tint_pad_5;
-  /* 0x0134 */ int8_t tint_pad_13[4];
+  /* 0x0134 */ tint_array<int8_t, 4> tint_pad_13;
   /* 0x0138 */ float2x2 tint_pad_27;
   /* 0x0148 */ uint tint_pad_24;
-  /* 0x014c */ int8_t tint_pad_14[4];
+  /* 0x014c */ tint_array<int8_t, 4> tint_pad_14;
   /* 0x0150 */ float2x3 tint_pad_23;
   /* 0x0170 */ uint tint_pad;
-  /* 0x0174 */ int8_t tint_pad_15[12];
+  /* 0x0174 */ tint_array<int8_t, 12> tint_pad_15;
   /* 0x0180 */ float2x4 tint_pad_8;
   /* 0x01a0 */ uint tint_pad_26;
-  /* 0x01a4 */ int8_t tint_pad_16[4];
+  /* 0x01a4 */ tint_array<int8_t, 4> tint_pad_16;
   /* 0x01a8 */ float3x2 tint_pad_29;
   /* 0x01c0 */ uint tint_pad_6;
-  /* 0x01c4 */ int8_t tint_pad_17[12];
+  /* 0x01c4 */ tint_array<int8_t, 12> tint_pad_17;
   /* 0x01d0 */ float3x3 tint_pad_22;
   /* 0x0200 */ uint tint_pad_32;
-  /* 0x0204 */ int8_t tint_pad_18[12];
+  /* 0x0204 */ tint_array<int8_t, 12> tint_pad_18;
   /* 0x0210 */ float3x4 tint_pad_34;
   /* 0x0240 */ uint tint_pad_35;
-  /* 0x0244 */ int8_t tint_pad_19[4];
+  /* 0x0244 */ tint_array<int8_t, 4> tint_pad_19;
   /* 0x0248 */ float4x2 tint_pad_30;
   /* 0x0268 */ uint tint_pad_9;
-  /* 0x026c */ int8_t tint_pad_36[4];
+  /* 0x026c */ tint_array<int8_t, 4> tint_pad_36;
   /* 0x0270 */ float4x3 tint_pad_31;
   /* 0x02b0 */ uint tint_pad_28;
-  /* 0x02b4 */ int8_t tint_pad_37[12];
+  /* 0x02b4 */ tint_array<int8_t, 12> tint_pad_37;
   /* 0x02c0 */ float4x4 tint_pad_4;
   /* 0x0300 */ float tint_pad_21;
-  /* 0x0304 */ int8_t tint_pad_38[124];
+  /* 0x0304 */ tint_array<int8_t, 124> tint_pad_38;
 };
 )");
 }
@@ -631,11 +661,11 @@
                                  Member("b", ty.f32()),
                              });
 
-    Global("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -798,11 +828,11 @@
     auto params = GetParam();
 
     auto* s = ty.storage_texture(params.dim, ast::TexelFormat::kR32Float, ast::Access::kWrite);
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/msl/generator_impl_unary_op_test.cc b/src/tint/writer/msl/generator_impl_unary_op_test.cc
index a1aecd9..843e2a4 100644
--- a/src/tint/writer/msl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/msl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using MslUnaryOpTest = TestHelper;
 
 TEST_F(MslUnaryOpTest, AddressOf) {
-    Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Complement) {
-    Global("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Indirection) {
-    Global("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
     auto* p =
         Let("expr", nullptr, create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
@@ -58,7 +58,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Not) {
-    Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -70,7 +70,7 @@
 }
 
 TEST_F(MslUnaryOpTest, Negation) {
-    Global("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index e41cc3b..46becb0 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -38,7 +38,7 @@
     EXPECT_EQ(gen.result(), "  float a = 0.0f;\n");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Let) {
     auto* var = Let("a", ty.f32(), Construct(ty.f32()));
     auto* stmt = Decl(var);
     WrapInFunction(stmt);
@@ -51,6 +51,266 @@
     EXPECT_EQ(gen.result(), "  float const a = 0.0f;\n");
 }
 
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const) {
+    auto* var = Const("a", ty.f32(), Construct(ty.f32()));
+    auto* stmt = Decl(var);
+    WrapInFunction(stmt);
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+    EXPECT_EQ(gen.result(), "");  // Not a mistake - 'const' is inlined
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int const l = 1;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  uint const l = 1u;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float const l = 1.0f;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  int3 const l = int3(1, 2, 3);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float3 const l = float3(1.0f, 2.0f, 3.0f);
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+  float2x3 const l = float2x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f));
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<float, 3> const l = tint_array<float, 3>{1.0f, 2.0f, 3.0f};
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void f() {
+  tint_array<bool2, 3> const l = tint_array<bool2, 3>{bool2(true, false), bool2(false, true), bool2(true)};
+}
+
+)");
+}
+
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Array) {
     auto* var = Var("a", ty.array<f32, 5>(), ast::StorageClass::kNone);
     auto* stmt = Decl(var);
@@ -61,7 +321,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
-    EXPECT_EQ(gen.result(), "  float a[5] = {0.0f};\n");
+    EXPECT_EQ(gen.result(), "  tint_array<float, 5> a = {};\n");
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Struct) {
@@ -111,7 +371,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     WrapInFunction(Expr("a"));
 
@@ -123,20 +383,8 @@
     EXPECT_THAT(gen.result(), HasSubstr("thread float tint_symbol_1 = 0.0f;\n"));
 }
 
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_Private) {
-    GlobalConst("initializer", ty.f32(), Expr(0_f));
-    Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
-
-    WrapInFunction(Expr("a"));
-
-    GeneratorImpl& gen = SanitizeAndBuild();
-
-    ASSERT_TRUE(gen.Generate()) << gen.error();
-    EXPECT_THAT(gen.result(), HasSubstr("thread float tint_symbol_1 = 0.0f;\n  float const tint_symbol = tint_symbol_1;\n  return;\n"));
-}
-
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Workgroup) {
-    Global("a", ty.f32(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kWorkgroup);
 
     WrapInFunction(Expr("a"));
 
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 4368e3c..833324a 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -103,12 +103,18 @@
     switch (builtin->Type()) {
         case BuiltinType::kAcos:
             return GLSLstd450Acos;
+        case BuiltinType::kAcosh:
+            return GLSLstd450Acosh;
         case BuiltinType::kAsin:
             return GLSLstd450Asin;
+        case BuiltinType::kAsinh:
+            return GLSLstd450Asinh;
         case BuiltinType::kAtan:
             return GLSLstd450Atan;
         case BuiltinType::kAtan2:
             return GLSLstd450Atan2;
+        case BuiltinType::kAtanh:
+            return GLSLstd450Atanh;
         case BuiltinType::kCeil:
             return GLSLstd450Ceil;
         case BuiltinType::kClamp:
@@ -527,7 +533,7 @@
             wgsize_ops.push_back(wgsize_result);
 
             // Generate OpConstant instructions for each dimension.
-            for (int i = 0; i < 3; i++) {
+            for (size_t i = 0; i < 3; i++) {
                 auto constant = ScalarConstant::U32(wgsize[i].value);
                 if (wgsize[i].overridable_const) {
                     // Make the constant specializable.
@@ -693,6 +699,12 @@
 }
 
 bool Builder::GenerateFunctionVariable(const ast::Variable* v) {
+    if (v->Is<ast::Const>()) {
+        // Constants are generated at their use. This is required as the 'const' declaration may be
+        // abstract-numeric, which has no SPIR-V type.
+        return true;
+    }
+
     uint32_t init_id = 0;
     if (v->constructor) {
         init_id = GenerateExpressionWithLoadIfNeeded(v->constructor);
@@ -703,9 +715,9 @@
 
     auto* sem = builder_.Sem().Get(v);
 
-    if (auto* let = v->As<ast::Let>()) {
-        if (!let->constructor) {
-            error_ = "missing constructor for constant";
+    if (v->Is<ast::Let>()) {
+        if (!v->constructor) {
+            error_ = "missing constructor for let";
             return false;
         }
         RegisterVariable(sem, init_id);
@@ -748,20 +760,18 @@
 }
 
 bool Builder::GenerateGlobalVariable(const ast::Variable* v) {
+    if (v->Is<ast::Const>()) {
+        // Constants are generated at their use. This is required as the 'const' declaration may be
+        // abstract-numeric, which has no SPIR-V type.
+        return true;
+    }
+
     auto* sem = builder_.Sem().Get(v);
     auto* type = sem->Type()->UnwrapRef();
 
     uint32_t init_id = 0;
     if (auto* ctor = v->constructor) {
-        if (!v->Is<ast::Override>()) {
-            auto* ctor_sem = builder_.Sem().Get(ctor);
-            if (auto constant = ctor_sem->ConstantValue()) {
-                init_id = GenerateConstantIfNeeded(std::move(constant));
-            }
-        }
-        if (init_id == 0) {
-            init_id = GenerateConstructorExpression(v, v->constructor);
-        }
+        init_id = GenerateConstructorExpression(v, ctor);
         if (init_id == 0) {
             return false;
         }
@@ -799,7 +809,7 @@
         }
     }
 
-    if (v->IsAnyOf<ast::Let, ast::Override>()) {
+    if (v->Is<ast::Override>()) {
         push_debug(spv::Op::OpName,
                    {Operand(init_id), Operand(builder_.Symbols().NameFor(v->symbol))});
 
@@ -949,7 +959,7 @@
                                     Operand(result_type_id),
                                     extract,
                                     Operand(info->source_id),
-                                    Operand(idx_constval.Element<uint32_t>(0)),
+                                    Operand(idx_constval->As<uint32_t>()),
                                 })) {
             return false;
         }
@@ -1280,8 +1290,16 @@
 
 uint32_t Builder::GenerateConstructorExpression(const ast::Variable* var,
                                                 const ast::Expression* expr) {
-    if (auto* literal = expr->As<ast::LiteralExpression>()) {
-        return GenerateLiteralIfNeeded(var, literal);
+    if (Is<ast::Override>(var)) {
+        if (auto* literal = expr->As<ast::LiteralExpression>()) {
+            return GenerateLiteralIfNeeded(var, literal);
+        }
+    } else {
+        if (auto* sem = builder_.Sem().Get(expr)) {
+            if (auto constant = sem->ConstantValue()) {
+                return GenerateConstantIfNeeded(constant);
+            }
+        }
     }
     if (auto* call = builder_.Sem().Get<sem::Call>(expr)) {
         if (call->Target()->IsAnyOf<sem::TypeConstructor, sem::TypeConversion>()) {
@@ -1362,6 +1380,15 @@
         }
     }
 
+    if (auto* res_mat = result_type->As<sem::Matrix>()) {
+        auto* value_type = args[0]->Type()->UnwrapRef();
+        if (auto* val_mat = value_type->As<sem::Matrix>()) {
+            // Generate passthrough for matrices of the same type
+            can_cast_or_copy =
+                (res_mat->columns() == val_mat->columns()) && (res_mat->rows() == val_mat->rows());
+        }
+    }
+
     if (can_cast_or_copy) {
         return GenerateCastOrCopyOrPassthrough(result_type, args[0]->Declaration(), global_var);
     }
@@ -1607,6 +1634,8 @@
         }
 
         return result_id;
+    } else if (from_type->Is<sem::Matrix>()) {
+        return val_id;
     } else {
         TINT_ICE(Writer, builder_.Diagnostics()) << "Invalid from_type";
     }
@@ -1674,93 +1703,34 @@
     return GenerateConstantIfNeeded(constant);
 }
 
-uint32_t Builder::GenerateConstantIfNeeded(const sem::Constant& constant) {
-    if (constant.AllZero()) {
-        return GenerateConstantNullIfNeeded(constant.Type());
+uint32_t Builder::GenerateConstantIfNeeded(const sem::Constant* constant) {
+    if (constant->AllZero()) {
+        return GenerateConstantNullIfNeeded(constant->Type());
     }
+    auto* ty = constant->Type();
 
-    static constexpr size_t kOpsResultIdx = 1;  // operand index of the result
-    auto& global_scope = scope_stack_[0];
-
-    auto gen_bool = [&](size_t element_idx) {
-        bool val = constant.Element<AInt>(element_idx);
-        return GenerateConstantIfNeeded(ScalarConstant::Bool(val));
-    };
-    auto gen_f32 = [&](size_t element_idx) {
-        auto val = f32(constant.Element<AFloat>(element_idx));
-        return GenerateConstantIfNeeded(ScalarConstant::F32(val.value));
-    };
-    auto gen_i32 = [&](size_t element_idx) {
-        auto val = i32(constant.Element<AInt>(element_idx));
-        return GenerateConstantIfNeeded(ScalarConstant::I32(val.value));
-    };
-    auto gen_u32 = [&](size_t element_idx) {
-        auto val = u32(constant.Element<AInt>(element_idx));
-        return GenerateConstantIfNeeded(ScalarConstant::U32(val.value));
-    };
-    auto gen_els = [&](std::vector<Operand>& ids, size_t start, size_t end, auto gen_el) {
-        for (size_t i = start; i < end; i++) {
-            auto id = gen_el(i);
-            if (!id) {
-                return false;
-            }
-            ids.emplace_back(id);
-        }
-        return true;
-    };
-    auto gen_vector = [&](const sem::Vector* ty, size_t start, size_t end) -> uint32_t {
+    auto composite = [&](size_t el_count) -> uint32_t {
         auto type_id = GenerateTypeIfNeeded(ty);
         if (!type_id) {
             return 0;
         }
 
+        static constexpr size_t kOpsResultIdx = 1;  // operand index of the result
+
         std::vector<Operand> ops;
-        ops.reserve(end - start + 2);
+        ops.reserve(el_count + 2);
         ops.emplace_back(type_id);
         ops.push_back(Operand(0u));  // Placeholder for the result ID
-        auto ok = Switch(
-            constant.ElementType(),                                                //
-            [&](const sem::Bool*) { return gen_els(ops, start, end, gen_bool); },  //
-            [&](const sem::F32*) { return gen_els(ops, start, end, gen_f32); },    //
-            [&](const sem::I32*) { return gen_els(ops, start, end, gen_i32); },    //
-            [&](const sem::U32*) { return gen_els(ops, start, end, gen_u32); },    //
-            [&](Default) {
-                error_ = "unhandled constant element type: " + builder_.FriendlyName(ty);
-                return false;
-            });
-        if (!ok) {
-            return 0;
-        }
 
-        return utils::GetOrCreate(global_scope.type_ctor_to_id_, OperandListKey{ops},
-                                  [&]() -> uint32_t {
-                                      auto result = result_op();
-                                      ops[kOpsResultIdx] = result;
-                                      push_type(spv::Op::OpConstantComposite, std::move(ops));
-                                      return std::get<uint32_t>(result);
-                                  });
-    };
-    auto gen_matrix = [&](const sem::Matrix* m) -> uint32_t {
-        auto mat_type_id = GenerateTypeIfNeeded(m);
-        if (!mat_type_id) {
-            return 0;
-        }
-
-        std::vector<Operand> ops;
-        ops.reserve(m->columns() + 2);
-        ops.emplace_back(mat_type_id);
-        ops.push_back(Operand(0u));  // Placeholder for the result ID
-
-        for (size_t column_idx = 0; column_idx < m->columns(); column_idx++) {
-            size_t start = m->rows() * column_idx;
-            size_t end = m->rows() * (column_idx + 1);
-            auto column_id = gen_vector(m->ColumnType(), start, end);
-            if (!column_id) {
+        for (size_t i = 0; i < el_count; i++) {
+            auto id = GenerateConstantIfNeeded(constant->Index(i));
+            if (!id) {
                 return 0;
             }
-            ops.emplace_back(column_id);
+            ops.emplace_back(id);
         }
 
+        auto& global_scope = scope_stack_[0];
         return utils::GetOrCreate(global_scope.type_ctor_to_id_, OperandListKey{ops},
                                   [&]() -> uint32_t {
                                       auto result = result_op();
@@ -1771,15 +1741,28 @@
     };
 
     return Switch(
-        constant.Type(),                                                                  //
-        [&](const sem::Bool*) { return gen_bool(0); },                                    //
-        [&](const sem::F32*) { return gen_f32(0); },                                      //
-        [&](const sem::I32*) { return gen_i32(0); },                                      //
-        [&](const sem::U32*) { return gen_u32(0); },                                      //
-        [&](const sem::Vector* v) { return gen_vector(v, 0, constant.ElementCount()); },  //
-        [&](const sem::Matrix* m) { return gen_matrix(m); },                              //
+        ty,  //
+        [&](const sem::Bool*) {
+            bool val = constant->As<bool>();
+            return GenerateConstantIfNeeded(ScalarConstant::Bool(val));
+        },
+        [&](const sem::F32*) {
+            auto val = constant->As<f32>();
+            return GenerateConstantIfNeeded(ScalarConstant::F32(val.value));
+        },
+        [&](const sem::I32*) {
+            auto val = constant->As<i32>();
+            return GenerateConstantIfNeeded(ScalarConstant::I32(val.value));
+        },
+        [&](const sem::U32*) {
+            auto val = constant->As<u32>();
+            return GenerateConstantIfNeeded(ScalarConstant::U32(val.value));
+        },
+        [&](const sem::Vector* v) { return composite(v->Width()); },
+        [&](const sem::Matrix* m) { return composite(m->columns()); },
+        [&](const sem::Array* a) { return composite(a->Count()); },
         [&](Default) {
-            error_ = "unhandled constant type: " + builder_.FriendlyName(constant.Type());
+            error_ = "unhandled constant type: " + builder_.FriendlyName(ty);
             return false;
         });
 }
@@ -2697,7 +2680,7 @@
     // Returns the argument with the given usage
     auto arg = [&](Usage usage) {
         int idx = signature.IndexOf(usage);
-        return (idx >= 0) ? arguments[idx] : nullptr;
+        return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
     };
 
     // Generates the argument with the given usage, returning the operand ID
@@ -3272,7 +3255,8 @@
                                                                      value,
                                                                  });
         case sem::BuiltinType::kAtomicCompareExchangeWeak: {
-            auto comparator = GenerateExpression(call->Arguments()[1]->Declaration());
+            auto comparator =
+                GenerateExpressionWithLoadIfNeeded(call->Arguments()[1]->Declaration());
             if (comparator == 0) {
                 return false;
             }
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index 9866328..f6bb93f 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -554,7 +554,7 @@
     /// Generates a constant value if needed
     /// @param constant the constant to generate.
     /// @returns the ID on success or 0 on failure
-    uint32_t GenerateConstantIfNeeded(const sem::Constant& constant);
+    uint32_t GenerateConstantIfNeeded(const sem::Constant* constant);
 
     /// Generates a scalar constant if needed
     /// @param constant the constant to generate.
diff --git a/src/tint/writer/spirv/builder_accessor_expression_test.cc b/src/tint/writer/spirv/builder_accessor_expression_test.cc
index 2df1b65..8996b8d 100644
--- a/src/tint/writer/spirv/builder_accessor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_accessor_expression_test.cc
@@ -22,193 +22,874 @@
 
 using BuilderTest = TestHelper;
 
-TEST_F(BuilderTest, IndexAccessor_VectorRef_Literal) {
-    // var ary : vec3<f32>;
-    // ary[1]  -> ref<f32>
+TEST_F(BuilderTest, Let_IndexAccessor_Vector) {
+    // let ary = vec3<i32>(1, 2, 3);
+    // var x = ary[1i];
 
-    auto* var = Var("ary", ty.vec3<f32>());
+    auto* ary = Let("ary", nullptr, vec3<i32>(1_i, 2_i, 3_i));
+    auto* x = Var("x", nullptr, IndexAccessor(ary, 1_i));
+    WrapInFunction(ary, x);
 
-    auto* ary = Expr("ary");
-    auto* idx_expr = Expr(1_i);
+    spirv::Builder& b = SanitizeAndBuild();
 
-    auto* expr = IndexAccessor(ary, idx_expr);
-    WrapInFunction(var, expr);
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 9u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
 %6 = OpTypeInt 32 1
-%7 = OpConstant %6 1
-%8 = OpTypePointer Function %4
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%9 = OpAccessChain %8 %1 %7
-)");
-}
-
-TEST_F(BuilderTest, IndexAccessor_VectorRef_Dynamic) {
-    // var ary : vec3<f32>;
-    // var idx : i32;
-    // ary[idx]  -> ref<f32>
-
-    auto* var = Var("ary", ty.vec3<f32>());
-    auto* idx = Var("idx", ty.i32());
-
-    auto* ary = Expr("ary");
-    auto* idx_expr = Expr("idx");
-
-    auto* expr = IndexAccessor(ary, idx_expr);
-    WrapInFunction(var, idx, expr);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-    ASSERT_TRUE(b.GenerateFunctionVariable(idx)) << b.error();
-
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 12u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%8 = OpTypeInt 32 1
-%7 = OpTypePointer Function %8
-%9 = OpConstantNull %8
-%11 = OpTypePointer Function %4
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-%6 = OpVariable %7 Function %9
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%10 = OpLoad %8 %6
-%12 = OpAccessChain %11 %1 %10
-)");
-}
-
-TEST_F(BuilderTest, IndexAccessor_VectorRef_Dynamic2) {
-    // var ary : vec3<f32>;
-    // ary[1 + 2]  -> ref<f32>
-
-    auto* var = Var("ary", ty.vec3<f32>());
-
-    auto* ary = Expr("ary");
-
-    auto* expr = IndexAccessor(ary, Add(1_i, 2_i));
-    WrapInFunction(var, expr);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 11u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%6 = OpTypeInt 32 1
+%5 = OpTypeVector %6 3
 %7 = OpConstant %6 1
 %8 = OpConstant %6 2
-%10 = OpTypePointer Function %4
+%9 = OpConstant %6 3
+%10 = OpConstantComposite %5 %7 %8 %9
+%13 = OpTypePointer Function %6
+%14 = OpConstantNull %6
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%12 = OpVariable %13 Function %14
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%9 = OpIAdd %6 %7 %8
-%11 = OpAccessChain %10 %1 %9
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%11 = OpCompositeExtract %6 %10 1
+OpStore %12 %11
+OpReturn
 )");
+
+    Validate(b);
 }
 
-TEST_F(BuilderTest, IndexAccessor_ArrayRef_MultiLevel) {
-    auto* ary4 = ty.array(ty.vec3<f32>(), 4_u);
+TEST_F(BuilderTest, Const_IndexAccessor_Vector) {
+    // const ary = vec3<i32>(1, 2, 3);
+    // var x = ary[1i];
 
-    // var ary : array<vec3<f32>, 4u>
-    // ary[3i][2i];
+    auto* ary = Const("ary", nullptr, vec3<i32>(1_i, 2_i, 3_i));
+    auto* x = Var("x", nullptr, IndexAccessor(ary, 1_i));
+    WrapInFunction(ary, x);
 
-    auto* var = Var("ary", ary4);
+    spirv::Builder& b = SanitizeAndBuild();
 
-    auto* expr = IndexAccessor(IndexAccessor("ary", 3_i), 2_i);
-    WrapInFunction(var, expr);
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 13u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
-%4 = OpTypeVector %5 3
-%6 = OpTypeInt 32 0
-%7 = OpConstant %6 4
-%3 = OpTypeArray %4 %7
-%2 = OpTypePointer Function %3
-%8 = OpConstantNull %3
-%9 = OpTypeInt 32 1
-%10 = OpConstant %9 3
-%11 = OpConstant %9 2
-%12 = OpTypePointer Function %5
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeInt 32 1
+%6 = OpConstant %5 2
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %8
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%7 = OpVariable %8 Function %9
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%13 = OpAccessChain %12 %1 %10 %11
+              R"(OpStore %7 %6
+OpReturn
 )");
+
+    Validate(b);
 }
 
-TEST_F(BuilderTest, IndexAccessor_ArrayRef_ArrayWithSwizzle) {
-    auto* ary4 = ty.array(ty.vec3<f32>(), 4_u);
+TEST_F(BuilderTest, Runtime_IndexAccessor_Vector) {
+    // var ary : vec3<u32>;
+    // var x = ary[1i];
 
-    // var a : array<vec3<f32>, 4u>;
-    // a[2i].xy;
+    auto* ary = Var("ary", ty.vec3<u32>());
+    auto* x = Var("x", nullptr, IndexAccessor(ary, 1_i));
+    WrapInFunction(ary, x);
 
-    auto* var = Var("ary", ary4);
+    spirv::Builder& b = SanitizeAndBuild();
 
-    auto* expr = MemberAccessor(IndexAccessor("ary", 2_i), "xy");
-    WrapInFunction(var, expr);
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 15u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
-%4 = OpTypeVector %5 3
-%6 = OpTypeInt 32 0
-%7 = OpConstant %6 4
-%3 = OpTypeArray %4 %7
-%2 = OpTypePointer Function %3
-%8 = OpConstantNull %3
-%9 = OpTypeInt 32 1
-%10 = OpConstant %9 2
-%11 = OpTypePointer Function %4
-%13 = OpTypeVector %5 2
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeInt 32 0
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%10 = OpTypeInt 32 1
+%11 = OpConstant %10 1
+%12 = OpTypePointer Function %8
+%16 = OpConstantNull %8
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %8
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+%15 = OpVariable %12 Function %16
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpAccessChain %12 %5 %11
+%14 = OpLoad %8 %13
+OpStore %15 %14
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Dynamic_IndexAccessor_Vector) {
+    // var ary : vec3<f32>;
+    // var idx : i32;
+    // var x = ary[idx];
+
+    auto* ary = Var("ary", ty.vec3<f32>());
+    auto* idx = Var("idx", ty.i32());
+    auto* x = Var("x", nullptr, IndexAccessor(ary, idx));
+    WrapInFunction(ary, idx, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%12 = OpTypeInt 32 1
+%11 = OpTypePointer Function %12
+%13 = OpConstantNull %12
+%15 = OpTypePointer Function %8
+%19 = OpConstantNull %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+%10 = OpVariable %11 Function %13
+%18 = OpVariable %15 Function %19
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%14 = OpLoad %12 %10
+%16 = OpAccessChain %15 %5 %14
+%17 = OpLoad %8 %16
+OpStore %18 %17
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Const_IndexAccessor_Vector2) {
+    // let ary : vec3<i32>(1, 2, 3);
+    // var x = ary[1i + 2i];
+
+    auto* ary = Let("ary", nullptr, vec3<i32>(1_i, 2_i, 3_i));
+    auto* x = Var("x", nullptr, IndexAccessor(ary, Add(1_i, 2_i)));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 1
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 1
+%8 = OpConstant %6 2
+%9 = OpConstant %6 3
+%10 = OpConstantComposite %5 %7 %8 %9
+%14 = OpTypePointer Function %6
+%15 = OpConstantNull %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%13 = OpVariable %14 Function %15
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%11 = OpIAdd %6 %7 %8
+%12 = OpVectorExtractDynamic %6 %10 %11
+OpStore %13 %12
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Runtime_IndexAccessor_Vector2) {
+    // var ary : vec3<f32>;
+    // var x = ary[1i + 2i];
+
+    auto* ary = Var("ary", ty.vec3<f32>());
+    auto* x = Var("x", nullptr, IndexAccessor(ary, Add(1_i, 2_i)));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%10 = OpTypeInt 32 1
+%11 = OpConstant %10 1
+%12 = OpConstant %10 2
+%14 = OpTypePointer Function %8
+%18 = OpConstantNull %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+%17 = OpVariable %14 Function %18
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpIAdd %10 %11 %12
+%15 = OpAccessChain %14 %5 %13
+%16 = OpLoad %8 %15
+OpStore %17 %16
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Dynamic_IndexAccessor_Vector2) {
+    // var ary : vec3<f32>;
+    // var one = 1i;
+    // var x = ary[one + 2i];
+
+    auto* ary = Var("ary", ty.vec3<f32>());
+    auto* one = Var("one", nullptr, Expr(1_i));
+    auto* x = Var("x", nullptr, IndexAccessor(ary, Add(one, 2_i)));
+    WrapInFunction(ary, one, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%10 = OpTypeInt 32 1
+%11 = OpConstant %10 1
+%13 = OpTypePointer Function %10
+%14 = OpConstantNull %10
+%16 = OpConstant %10 2
+%18 = OpTypePointer Function %8
+%22 = OpConstantNull %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+%12 = OpVariable %13 Function %14
+%21 = OpVariable %18 Function %22
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %12 %11
+%15 = OpLoad %10 %12
+%17 = OpIAdd %10 %15 %16
+%19 = OpAccessChain %18 %5 %17
+%20 = OpLoad %8 %19
+OpStore %21 %20
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Let_IndexAccessor_Array_MultiLevel) {
+    // let ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
+    // var x = ary[1i][2i];
+
+    auto* ary =
+        Let("ary", nullptr,
+            array(ty.vec3<f32>(), 2_u, vec3<f32>(1._f, 2._f, 3._f), vec3<f32>(4._f, 5._f, 6._f)));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, 1_i), 2_i));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 3
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 2
+%5 = OpTypeArray %6 %9
+%10 = OpConstant %7 1
+%11 = OpConstant %7 2
+%12 = OpConstant %7 3
+%13 = OpConstantComposite %6 %10 %11 %12
+%14 = OpConstant %7 4
+%15 = OpConstant %7 5
+%16 = OpConstant %7 6
+%17 = OpConstantComposite %6 %14 %15 %16
+%18 = OpConstantComposite %5 %13 %17
+%19 = OpTypeInt 32 1
+%20 = OpConstant %19 1
+%22 = OpConstant %19 2
+%25 = OpTypePointer Function %7
+%26 = OpConstantNull %7
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%24 = OpVariable %25 Function %26
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%12 = OpAccessChain %11 %1 %10
-%14 = OpLoad %4 %12
-%15 = OpVectorShuffle %13 %14 %14 0 1
+              R"(%21 = OpCompositeExtract %6 %18 1
+%23 = OpCompositeExtract %7 %21 2
+OpStore %24 %23
+OpReturn
 )");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Const_IndexAccessor_Array_MultiLevel) {
+    // const ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
+    // var x = ary[1i][2i];
+
+    auto* ary =
+        Const("ary", nullptr,
+              array(ty.vec3<f32>(), 2_u, vec3<f32>(1._f, 2._f, 3._f), vec3<f32>(4._f, 5._f, 6._f)));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, 1_i), 2_i));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 6
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%7 = OpVariable %8 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %7 %6
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Runtime_IndexAccessor_Array_MultiLevel) {
+    // var ary : array<vec3<f32>, 4u>;
+    // var x = ary[1i][2i];
+
+    auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, 1_i), 2_i));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 3
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 4
+%7 = OpTypeArray %8 %11
+%6 = OpTypePointer Function %7
+%12 = OpConstantNull %7
+%13 = OpTypeInt 32 1
+%14 = OpConstant %13 1
+%15 = OpConstant %13 2
+%16 = OpTypePointer Function %9
+%20 = OpConstantNull %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
+%19 = OpVariable %16 Function %20
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%17 = OpAccessChain %16 %5 %14 %15
+%18 = OpLoad %9 %17
+OpStore %19 %18
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Dynamic_IndexAccessor_Array_MultiLevel) {
+    // var ary : array<vec3<f32>, 4u>;
+    // var one = 1i;
+    // var x = ary[one][2i];
+
+    auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
+    auto* one = Var("one", nullptr, Expr(3_i));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, one), 2_i));
+    WrapInFunction(ary, one, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 3
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 4
+%7 = OpTypeArray %8 %11
+%6 = OpTypePointer Function %7
+%12 = OpConstantNull %7
+%13 = OpTypeInt 32 1
+%14 = OpConstant %13 3
+%16 = OpTypePointer Function %13
+%17 = OpConstantNull %13
+%19 = OpConstant %13 2
+%20 = OpTypePointer Function %9
+%24 = OpConstantNull %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
+%15 = OpVariable %16 Function %17
+%23 = OpVariable %20 Function %24
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %15 %14
+%18 = OpLoad %13 %15
+%21 = OpAccessChain %20 %5 %18 %19
+%22 = OpLoad %9 %21
+OpStore %23 %22
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Const_IndexAccessor_Array_ArrayWithSwizzle) {
+    // let ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
+    // var x = a[1i].xy;
+
+    auto* ary =
+        Let("ary", nullptr,
+            array(ty.vec3<f32>(), 2_u, vec3<f32>(1._f, 2._f, 3._f), vec3<f32>(4._f, 5._f, 6._f)));
+    auto* x = Var("x", nullptr, MemberAccessor(IndexAccessor("ary", 1_i), "xy"));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 3
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 2
+%5 = OpTypeArray %6 %9
+%10 = OpConstant %7 1
+%11 = OpConstant %7 2
+%12 = OpConstant %7 3
+%13 = OpConstantComposite %6 %10 %11 %12
+%14 = OpConstant %7 4
+%15 = OpConstant %7 5
+%16 = OpConstant %7 6
+%17 = OpConstantComposite %6 %14 %15 %16
+%18 = OpConstantComposite %5 %13 %17
+%19 = OpTypeInt 32 1
+%20 = OpConstant %19 1
+%22 = OpTypeVector %7 2
+%25 = OpTypePointer Function %22
+%26 = OpConstantNull %22
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%24 = OpVariable %25 Function %26
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%21 = OpCompositeExtract %6 %18 1
+%23 = OpVectorShuffle %22 %21 %21 0 1
+OpStore %24 %23
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Runtime_IndexAccessor_Array_ArrayWithSwizzle) {
+    // var ary : array<vec3<f32>, 4u>;
+    // var x = ary[1i].xy;
+
+    auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
+    auto* x = Var("x", nullptr, MemberAccessor(IndexAccessor("ary", 1_i), "xy"));
+    WrapInFunction(ary, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 3
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 4
+%7 = OpTypeArray %8 %11
+%6 = OpTypePointer Function %7
+%12 = OpConstantNull %7
+%13 = OpTypeInt 32 1
+%14 = OpConstant %13 1
+%15 = OpTypePointer Function %8
+%17 = OpTypeVector %9 2
+%21 = OpTypePointer Function %17
+%22 = OpConstantNull %17
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
+%20 = OpVariable %21 Function %22
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%16 = OpAccessChain %15 %5 %14
+%18 = OpLoad %8 %16
+%19 = OpVectorShuffle %17 %18 %18 0 1
+OpStore %20 %19
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Dynamic_IndexAccessor_Array_ArrayWithSwizzle) {
+    // var ary : array<vec3<f32>, 4u>;
+    // var one = 1i;
+    // var x = ary[one].xy;
+
+    auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
+    auto* one = Var("one", nullptr, Expr(1_i));
+    auto* x = Var("x", nullptr, MemberAccessor(IndexAccessor("ary", one), "xy"));
+    WrapInFunction(ary, one, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 3
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 4
+%7 = OpTypeArray %8 %11
+%6 = OpTypePointer Function %7
+%12 = OpConstantNull %7
+%13 = OpTypeInt 32 1
+%14 = OpConstant %13 1
+%16 = OpTypePointer Function %13
+%17 = OpConstantNull %13
+%19 = OpTypePointer Function %8
+%21 = OpTypeVector %9 2
+%25 = OpTypePointer Function %21
+%26 = OpConstantNull %21
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
+%15 = OpVariable %16 Function %17
+%24 = OpVariable %25 Function %26
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %15 %14
+%18 = OpLoad %13 %15
+%20 = OpAccessChain %19 %5 %18
+%22 = OpLoad %8 %20
+%23 = OpVectorShuffle %21 %22 %22 0 1
+OpStore %24 %23
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Let_IndexAccessor_Nested_Array_f32) {
+    // let pos : array<array<f32, 2>, 3u> = array<vec2<f32, 2>, 3u>(
+    //   array<f32, 2>(0.0, 0.5),
+    //   array<f32, 2>(-0.5, -0.5),
+    //   array<f32, 2>(0.5, -0.5));
+    // var x = pos[1u][0u];
+
+    auto* pos = Let("pos", ty.array(ty.vec2<f32>(), 3_u),
+                    Construct(ty.array(ty.vec2<f32>(), 3_u), vec2<f32>(0_f, 0.5_f),
+                              vec2<f32>(-0.5_f, -0.5_f), vec2<f32>(0.5_f, -0.5_f)));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, 1_u), 0_u));
+    WrapInFunction(pos, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%8 = OpTypeInt 32 0
+%9 = OpConstant %8 3
+%5 = OpTypeArray %6 %9
+%10 = OpConstantNull %7
+%11 = OpConstant %7 0.5
+%12 = OpConstantComposite %6 %10 %11
+%13 = OpConstant %7 -0.5
+%14 = OpConstantComposite %6 %13 %13
+%15 = OpConstantComposite %6 %11 %13
+%16 = OpConstantComposite %5 %12 %14 %15
+%17 = OpConstant %8 1
+%19 = OpConstantNull %8
+%22 = OpTypePointer Function %7
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%21 = OpVariable %22 Function %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%18 = OpCompositeExtract %6 %16 1
+%20 = OpCompositeExtract %7 %18 0
+OpStore %21 %20
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Const_IndexAccessor_Nested_Array_f32) {
+    // const pos : array<array<f32, 2>, 3u> = array<vec2<f32, 2>, 3u>(
+    //   array<f32, 2>(0.0, 0.5),
+    //   array<f32, 2>(-0.5, -0.5),
+    //   array<f32, 2>(0.5, -0.5));
+    // var x = pos[1u][0u];
+
+    auto* pos = Const("pos", ty.array(ty.vec2<f32>(), 3_u),
+                      Construct(ty.array(ty.vec2<f32>(), 3_u), vec2<f32>(0_f, 0.5_f),
+                                vec2<f32>(-0.5_f, -0.5_f), vec2<f32>(0.5_f, -0.5_f)));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, 1_u), 0_u));
+    WrapInFunction(pos, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 -0.5
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%7 = OpVariable %8 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %7 %6
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Runtime_IndexAccessor_Nested_Array_f32) {
+    // var pos : array<array<f32, 2>, 3u>;
+    // var x = pos[1u][2u];
+
+    auto* pos = Var("pos", ty.array(ty.vec2<f32>(), 3_u));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, 1_u), 2_u));
+    WrapInFunction(pos, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 2
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 3
+%7 = OpTypeArray %8 %11
+%6 = OpTypePointer Function %7
+%12 = OpConstantNull %7
+%13 = OpConstant %10 1
+%14 = OpConstant %10 2
+%15 = OpTypePointer Function %9
+%19 = OpConstantNull %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
+%18 = OpVariable %15 Function %19
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(%16 = OpAccessChain %15 %5 %13 %14
+%17 = OpLoad %9 %16
+OpStore %18 %17
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Dynamic_IndexAccessor_Nested_Array_f32) {
+    // var pos : array<array<f32, 2>, 3u>;
+    // var one = 1u;
+    // var x = pos[one][2u];
+
+    auto* pos = Var("pos", ty.array(ty.vec2<f32>(), 3_u));
+    auto* one = Var("one", nullptr, Expr(2_u));
+    auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, "one"), 2_u));
+    WrapInFunction(pos, one, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 2
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 3
+%7 = OpTypeArray %8 %11
+%6 = OpTypePointer Function %7
+%12 = OpConstantNull %7
+%13 = OpConstant %10 2
+%15 = OpTypePointer Function %10
+%16 = OpConstantNull %10
+%18 = OpTypePointer Function %9
+%22 = OpConstantNull %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
+%14 = OpVariable %15 Function %16
+%21 = OpVariable %18 Function %22
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %14 %13
+%17 = OpLoad %10 %14
+%19 = OpAccessChain %18 %5 %17 %13
+%20 = OpLoad %9 %19
+OpStore %21 %20
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Let_IndexAccessor_Matrix) {
+    // let a : mat2x2<f32>(vec2<f32>(1., 2.), vec2<f32>(3., 4.));
+    // var x = a[1i]
+
+    auto* a = Let("a", ty.mat2x2<f32>(),
+                  Construct(ty.mat2x2<f32>(), Construct(ty.vec2<f32>(), 1_f, 2_f),
+                            Construct(ty.vec2<f32>(), 3_f, 4_f)));
+    auto* x = Var("x", nullptr, IndexAccessor("a", 1_i));
+    WrapInFunction(a, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%5 = OpTypeMatrix %6 2
+%8 = OpConstant %7 1
+%9 = OpConstant %7 2
+%10 = OpConstantComposite %6 %8 %9
+%11 = OpConstant %7 3
+%12 = OpConstant %7 4
+%13 = OpConstantComposite %6 %11 %12
+%14 = OpConstantComposite %5 %10 %13
+%15 = OpTypeInt 32 1
+%16 = OpConstant %15 1
+%19 = OpTypePointer Function %6
+%20 = OpConstantNull %6
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%18 = OpVariable %19 Function %20
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%17 = OpCompositeExtract %6 %14 1
+OpStore %18 %17
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Const_IndexAccessor_Matrix) {
+    // const a : mat2x2<f32>(vec2<f32>(1., 2.), vec2<f32>(3., 4.));
+    // var x = a[1i]
+
+    auto* a = Const("a", ty.mat2x2<f32>(),
+                    Construct(ty.mat2x2<f32>(), Construct(ty.vec2<f32>(), 1_f, 2_f),
+                              Construct(ty.vec2<f32>(), 3_f, 4_f)));
+    auto* x = Var("x", nullptr, IndexAccessor("a", 1_i));
+    WrapInFunction(a, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 3
+%8 = OpConstant %6 4
+%9 = OpConstantComposite %5 %7 %8
+%11 = OpTypePointer Function %5
+%12 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%10 = OpVariable %11 Function %12
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %10 %9
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Runtime_IndexAccessor_Matrix) {
+    // var a : mat2x2<f32>;
+    // var x = a[1i]
+
+    auto* a = Var("a", ty.mat2x2<f32>());
+    auto* x = Var("x", nullptr, IndexAccessor("a", 1_i));
+    WrapInFunction(a, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 2
+%7 = OpTypeMatrix %8 2
+%6 = OpTypePointer Function %7
+%10 = OpConstantNull %7
+%11 = OpTypeInt 32 1
+%12 = OpConstant %11 1
+%13 = OpTypePointer Function %8
+%17 = OpConstantNull %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
+%16 = OpVariable %13 Function %17
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%14 = OpAccessChain %13 %5 %12
+%15 = OpLoad %8 %14
+OpStore %16 %15
+OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, Dynamic_IndexAccessor_Matrix) {
+    // var a : mat2x2<f32>;
+    // var idx : i32
+    // var x = a[idx]
+
+    auto* a = Var("a", ty.mat2x2<f32>());
+    auto* idx = Var("idx", ty.i32());
+    auto* x = Var("x", nullptr, IndexAccessor("a", idx));
+    WrapInFunction(a, idx, x);
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 2
+%7 = OpTypeMatrix %8 2
+%6 = OpTypePointer Function %7
+%10 = OpConstantNull %7
+%13 = OpTypeInt 32 1
+%12 = OpTypePointer Function %13
+%14 = OpConstantNull %13
+%16 = OpTypePointer Function %8
+%20 = OpConstantNull %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
+%11 = OpVariable %12 Function %14
+%19 = OpVariable %16 Function %20
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%15 = OpLoad %13 %11
+%17 = OpAccessChain %16 %5 %15
+%18 = OpLoad %8 %17
+OpStore %19 %18
+OpReturn
+)");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor) {
@@ -229,27 +910,28 @@
     auto* expr = MemberAccessor("ident", "b");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 9u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeStruct %8 %8
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 1
+%12 = OpTypePointer Function %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpAccessChain %12 %5 %11
+%14 = OpLoad %8 %13
+OpReturn
+)");
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeStruct %4 %4
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%6 = OpTypeInt 32 0
-%7 = OpConstant %6 1
-%8 = OpTypePointer Function %4
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%9 = OpAccessChain %8 %1 %7
-)");
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Nested) {
@@ -274,29 +956,31 @@
     auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "b");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 11u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
-%4 = OpTypeStruct %5 %5
-%3 = OpTypeStruct %4
-%2 = OpTypePointer Function %3
-%6 = OpConstantNull %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%9 = OpConstant %7 1
-%10 = OpTypePointer Function %5
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeStruct %9 %9
+%7 = OpTypeStruct %8
+%6 = OpTypePointer Function %7
+%10 = OpConstantNull %7
+%11 = OpTypeInt 32 0
+%12 = OpConstant %11 0
+%13 = OpConstant %11 1
+%14 = OpTypePointer Function %9
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %6
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%11 = OpAccessChain %10 %1 %8 %9
+              R"(%15 = OpAccessChain %14 %5 %12 %13
+%16 = OpLoad %9 %15
+OpReturn
 )");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_NonPointer) {
@@ -317,21 +1001,23 @@
     auto* expr = MemberAccessor("ident", "b");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 5u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeStruct %2 %2
-%3 = OpConstantNull %2
-%4 = OpConstantComposite %1 %3 %3
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeStruct %6 %6
+%7 = OpConstantNull %6
+%8 = OpConstantComposite %5 %7 %7
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%5 = OpCompositeExtract %2 %4 1
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%9 = OpCompositeExtract %6 %8 1
+OpReturn
 )");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Nested_NonPointer) {
@@ -357,24 +1043,27 @@
     auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "b");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 8u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypeStruct %3 %3
-%1 = OpTypeStruct %2
-%4 = OpConstantNull %3
-%5 = OpConstantComposite %2 %4 %4
-%6 = OpConstantComposite %1 %5
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeStruct %7 %7
+%5 = OpTypeStruct %6
+%8 = OpConstantNull %7
+%9 = OpConstantComposite %6 %8 %8
+%10 = OpConstantComposite %5 %9
 )");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%7 = OpCompositeExtract %2 %6 0
-%8 = OpCompositeExtract %3 %7 1
+              R"(%11 = OpCompositeExtract %6 %10 0
+%12 = OpCompositeExtract %7 %11 1
+OpReturn
 )");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Nested_WithAlias) {
@@ -401,28 +1090,30 @@
     auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "a");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 10u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
-%4 = OpTypeStruct %5 %5
-%3 = OpTypeStruct %4
-%2 = OpTypePointer Function %3
-%6 = OpConstantNull %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%9 = OpTypePointer Function %5
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeStruct %9 %9
+%7 = OpTypeStruct %8
+%6 = OpTypePointer Function %7
+%10 = OpConstantNull %7
+%11 = OpTypeInt 32 0
+%12 = OpConstant %11 0
+%13 = OpTypePointer Function %9
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %6
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%10 = OpAccessChain %9 %1 %8 %8
+              R"(%14 = OpAccessChain %13 %5 %12 %12
+%15 = OpLoad %9 %14
+OpReturn
 )");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_LHS) {
@@ -446,30 +1137,31 @@
     auto* expr = Assign(MemberAccessor(MemberAccessor("ident", "inner"), "a"), Expr(2_f));
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_TRUE(b.GenerateAssignStatement(expr)) << b.error();
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
-%4 = OpTypeStruct %5 %5
-%3 = OpTypeStruct %4
-%2 = OpTypePointer Function %3
-%6 = OpConstantNull %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%9 = OpTypePointer Function %5
-%11 = OpConstant %5 2
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeStruct %9 %9
+%7 = OpTypeStruct %8
+%6 = OpTypePointer Function %7
+%10 = OpConstantNull %7
+%11 = OpTypeInt 32 0
+%12 = OpConstant %11 0
+%13 = OpTypePointer Function %9
+%15 = OpConstant %9 2
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %6
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%10 = OpAccessChain %9 %1 %8 %8
-OpStore %10 %11
+              R"(%14 = OpAccessChain %13 %5 %12 %12
+OpStore %14 %15
+OpReturn
 )");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_RHS) {
@@ -497,33 +1189,33 @@
     auto* expr = Assign("store", rhs);
     WrapInFunction(var, store, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-    ASSERT_TRUE(b.GenerateFunctionVariable(store)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_TRUE(b.GenerateAssignStatement(expr)) << b.error();
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
-%4 = OpTypeStruct %5 %5
-%3 = OpTypeStruct %4
-%2 = OpTypePointer Function %3
-%6 = OpConstantNull %3
-%8 = OpTypePointer Function %5
-%9 = OpConstantNull %5
-%10 = OpTypeInt 32 0
-%11 = OpConstant %10 0
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeStruct %9 %9
+%7 = OpTypeStruct %8
+%6 = OpTypePointer Function %7
+%10 = OpConstantNull %7
+%12 = OpTypePointer Function %9
+%13 = OpConstantNull %9
+%14 = OpTypeInt 32 0
+%15 = OpConstant %14 0
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %6
-%7 = OpVariable %8 Function %9
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
+%11 = OpVariable %12 Function %13
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%12 = OpAccessChain %8 %1 %11 %11
-%13 = OpLoad %5 %12
-OpStore %7 %13
+              R"(%16 = OpAccessChain %12 %5 %15 %15
+%17 = OpLoad %9 %16
+OpStore %11 %17
+OpReturn
 )");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Swizzle_Single) {
@@ -534,27 +1226,28 @@
     auto* expr = MemberAccessor("ident", "y");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 9u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%10 = OpTypeInt 32 0
+%11 = OpConstant %10 1
+%12 = OpTypePointer Function %8
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpAccessChain %12 %5 %11
+%14 = OpLoad %8 %13
+OpReturn
+)");
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%6 = OpTypeInt 32 0
-%7 = OpConstant %6 1
-%8 = OpTypePointer Function %4
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%9 = OpAccessChain %8 %1 %7
-)");
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Swizzle_MultipleNames) {
@@ -565,26 +1258,26 @@
     auto* expr = MemberAccessor("ident", "yx");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 8u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%10 = OpTypeVector %8 2
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%11 = OpLoad %7 %5
+%12 = OpVectorShuffle %10 %11 %11 1 0
+OpReturn
+)");
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%6 = OpTypeVector %4 2
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%7 = OpLoad %3 %1
-%8 = OpVectorShuffle %6 %7 %7 1 0
-)");
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Swizzle_of_Swizzle) {
@@ -595,27 +1288,27 @@
     auto* expr = MemberAccessor(MemberAccessor("ident", "yxz"), "xz");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 9u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%12 = OpTypeVector %8 2
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
+%11 = OpVectorShuffle %7 %10 %10 1 0 2
+%13 = OpVectorShuffle %12 %11 %11 0 2
+OpReturn
+)");
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%8 = OpTypeVector %4 2
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%6 = OpLoad %3 %1
-%7 = OpVectorShuffle %3 %6 %6 1 0 2
-%9 = OpVectorShuffle %8 %7 %7 0 2
-)");
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Member_of_Swizzle) {
@@ -626,26 +1319,26 @@
     auto* expr = MemberAccessor(MemberAccessor("ident", "yxz"), "x");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 8u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
+%11 = OpVectorShuffle %7 %10 %10 1 0 2
+%12 = OpCompositeExtract %8 %11 0
+OpReturn
+)");
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%6 = OpLoad %3 %1
-%7 = OpVectorShuffle %3 %6 %6 1 0 2
-%8 = OpCompositeExtract %4 %7 0
-)");
+    Validate(b);
 }
 
 TEST_F(BuilderTest, MemberAccessor_Array_of_Swizzle) {
@@ -656,28 +1349,28 @@
     auto* expr = IndexAccessor(MemberAccessor("ident", "yxz"), 1_i);
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 10u);
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%8 = OpTypeFloat 32
+%7 = OpTypeVector %8 3
+%6 = OpTypePointer Function %7
+%9 = OpConstantNull %7
+%12 = OpTypeInt 32 1
+%13 = OpConstant %12 1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
+%11 = OpVectorShuffle %7 %10 %10 1 0 2
+%14 = OpCompositeExtract %8 %11 1
+OpReturn
+)");
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Function %3
-%5 = OpConstantNull %3
-%8 = OpTypeInt 32 1
-%9 = OpConstant %8 1
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %5
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%6 = OpLoad %3 %1
-%7 = OpVectorShuffle %3 %6 %6 1 0 2
-%10 = OpCompositeExtract %4 %7 1
-)");
+    Validate(b);
 }
 
 TEST_F(BuilderTest, IndexAccessor_Mixed_ArrayAndMember) {
@@ -709,323 +1402,37 @@
         "yx");
     WrapInFunction(var, expr);
 
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateFunctionVariable(var)) << b.error();
-
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 22u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%9 = OpTypeFloat 32
-%8 = OpTypeVector %9 3
-%7 = OpTypeStruct %8
-%6 = OpTypeStruct %7
-%10 = OpTypeInt 32 0
-%11 = OpConstant %10 3
-%5 = OpTypeArray %6 %11
-%4 = OpTypeStruct %5
-%12 = OpConstant %10 2
-%3 = OpTypeArray %4 %12
-%2 = OpTypePointer Function %3
-%13 = OpConstantNull %3
-%14 = OpTypeInt 32 1
-%15 = OpConstantNull %14
-%16 = OpConstant %10 0
-%17 = OpConstant %14 2
-%18 = OpTypePointer Function %8
-%20 = OpTypeVector %9 2
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%1 = OpVariable %2 Function %13
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%19 = OpAccessChain %18 %1 %15 %16 %17 %16 %16
-%21 = OpLoad %8 %19
-%22 = OpVectorShuffle %20 %21 %21 1 0
-)");
-}
-
-TEST_F(BuilderTest, IndexAccessor_Of_Vec) {
-    // let pos : array<vec2<f32>, 3u> = array<vec2<f32>, 3u>(
-    //   vec2<f32>(0.0, 0.5),
-    //   vec2<f32>(-0.5, -0.5),
-    //   vec2<f32>(0.5, -0.5));
-    // pos[1u]
-
-    auto* var = Let("pos", ty.array(ty.vec2<f32>(), 3_u),
-                    Construct(ty.array(ty.vec2<f32>(), 3_u), vec2<f32>(0_f, 0.5_f),
-                              vec2<f32>(-0.5_f, -0.5_f), vec2<f32>(0.5_f, -0.5_f)));
-
-    auto* expr = IndexAccessor("pos", 1_u);
-    WrapInFunction(var, expr);
-
     spirv::Builder& b = SanitizeAndBuild();
 
-    ASSERT_TRUE(b.Build());
+    ASSERT_TRUE(b.Build()) << b.error();
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
 %1 = OpTypeFunction %2
-%7 = OpTypeFloat 32
-%6 = OpTypeVector %7 2
-%8 = OpTypeInt 32 0
-%9 = OpConstant %8 3
-%5 = OpTypeArray %6 %9
-%10 = OpConstant %7 0
-%11 = OpConstant %7 0.5
-%12 = OpConstantComposite %6 %10 %11
-%13 = OpConstant %7 -0.5
-%14 = OpConstantComposite %6 %13 %13
-%15 = OpConstantComposite %6 %11 %13
-%16 = OpConstantComposite %5 %12 %14 %15
-%17 = OpConstant %8 1
+%13 = OpTypeFloat 32
+%12 = OpTypeVector %13 3
+%11 = OpTypeStruct %12
+%10 = OpTypeStruct %11
+%14 = OpTypeInt 32 0
+%15 = OpConstant %14 3
+%9 = OpTypeArray %10 %15
+%8 = OpTypeStruct %9
+%16 = OpConstant %14 2
+%7 = OpTypeArray %8 %16
+%6 = OpTypePointer Function %7
+%17 = OpConstantNull %7
+%18 = OpTypeInt 32 1
+%19 = OpConstantNull %18
+%20 = OpConstant %14 0
+%21 = OpConstant %18 2
+%22 = OpTypePointer Function %12
+%24 = OpTypeVector %13 2
 )");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%18 = OpCompositeExtract %6 %16 1
-OpReturn
-)");
-
-    Validate(b);
-}
-
-TEST_F(BuilderTest, IndexAccessor_Of_Array_Of_f32) {
-    // let pos : array<array<f32, 2>, 3u> = array<vec2<f32, 2>, 3u>(
-    //   array<f32, 2>(0.0, 0.5),
-    //   array<f32, 2>(-0.5, -0.5),
-    //   array<f32, 2>(0.5, -0.5));
-    // pos[2u][1u]
-
-    auto* var = Let("pos", ty.array(ty.vec2<f32>(), 3_u),
-                    Construct(ty.array(ty.vec2<f32>(), 3_u), vec2<f32>(0_f, 0.5_f),
-                              vec2<f32>(-0.5_f, -0.5_f), vec2<f32>(0.5_f, -0.5_f)));
-
-    auto* expr = IndexAccessor(IndexAccessor("pos", 2_u), 1_u);
-    WrapInFunction(var, expr);
-
-    spirv::Builder& b = SanitizeAndBuild();
-
-    ASSERT_TRUE(b.Build());
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
-%1 = OpTypeFunction %2
-%7 = OpTypeFloat 32
-%6 = OpTypeVector %7 2
-%8 = OpTypeInt 32 0
-%9 = OpConstant %8 3
-%5 = OpTypeArray %6 %9
-%10 = OpConstant %7 0
-%11 = OpConstant %7 0.5
-%12 = OpConstantComposite %6 %10 %11
-%13 = OpConstant %7 -0.5
-%14 = OpConstantComposite %6 %13 %13
-%15 = OpConstantComposite %6 %11 %13
-%16 = OpConstantComposite %5 %12 %14 %15
-%17 = OpConstant %8 2
-%19 = OpConstant %8 1
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%18 = OpCompositeExtract %6 %16 2
-%20 = OpCompositeExtract %7 %18 1
-OpReturn
-)");
-
-    Validate(b);
-}
-
-TEST_F(BuilderTest, IndexAccessor_Vec_Literal) {
-    // let pos : vec2<f32> = vec2<f32>(0.0, 0.5);
-    // pos[1]
-
-    auto* var = Let("pos", ty.vec2<f32>(), vec2<f32>(0_f, 0.5_f));
-
-    auto* expr = IndexAccessor("pos", 1_u);
-    WrapInFunction(var, expr);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 8u) << b.error();
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 2
-%3 = OpConstant %2 0
-%4 = OpConstant %2 0.5
-%5 = OpConstantComposite %1 %3 %4
-%6 = OpTypeInt 32 0
-%7 = OpConstant %6 1
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), "");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%8 = OpCompositeExtract %2 %5 1
-)");
-}
-
-TEST_F(BuilderTest, IndexAccessor_Vec_Dynamic) {
-    // let pos : vec2<f32> = vec2<f32>(0.0, 0.5);
-    // idx : i32
-    // pos[idx]
-
-    auto* var = Let("pos", ty.vec2<f32>(), vec2<f32>(0_f, 0.5_f));
-    auto* idx = Var("idx", ty.i32());
-    auto* expr = IndexAccessor("pos", idx);
-
-    WrapInFunction(var, idx, expr);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-    ASSERT_TRUE(b.GenerateFunctionVariable(idx)) << b.error();
-    EXPECT_EQ(b.GenerateAccessorExpression(expr), 11u) << b.error();
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 2
-%3 = OpConstant %2 0
-%4 = OpConstant %2 0.5
-%5 = OpConstantComposite %1 %3 %4
-%8 = OpTypeInt 32 1
-%7 = OpTypePointer Function %8
-%9 = OpConstantNull %8
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%6 = OpVariable %7 Function %9
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %17
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%10 = OpLoad %8 %6
-%11 = OpVectorExtractDynamic %2 %5 %10
-)");
-}
-
-TEST_F(BuilderTest, IndexAccessor_Array_Literal) {
-    // let a : array<f32, 3u>;
-    // a[2i]
-
-    auto* var = Let("a", ty.array<f32, 3>(), Construct(ty.array<f32, 3>(), 0_f, 0.5_f, 1_f));
-    auto* expr = IndexAccessor("a", 2_i);
-    WrapInFunction(var, expr);
-
-    spirv::Builder& b = SanitizeAndBuild();
-
-    ASSERT_TRUE(b.Build());
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
-%1 = OpTypeFunction %2
-%6 = OpTypeFloat 32
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 3
-%5 = OpTypeArray %6 %8
-%9 = OpConstantNull %6
-%10 = OpConstant %6 0.5
-%11 = OpConstant %6 1
-%12 = OpConstantComposite %5 %9 %10 %11
-%13 = OpTypeInt 32 1
-%14 = OpConstant %13 2
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), "");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(%15 = OpCompositeExtract %6 %12 2
-OpReturn
-)");
-
-    Validate(b);
-}
-
-TEST_F(BuilderTest, IndexAccessor_Array_Dynamic) {
-    // let a : array<f32, 3>;
-    // idx : i32
-    // a[idx]
-
-    auto* var = Let("a", ty.array<f32, 3>(), Construct(ty.array<f32, 3>(), 0_f, 0.5_f, 1_f));
-
-    auto* idx = Var("idx", ty.i32());
-    auto* expr = IndexAccessor("a", idx);
-
-    WrapInFunction(var, idx, expr);
-
-    spirv::Builder& b = SanitizeAndBuild();
-
-    ASSERT_TRUE(b.Build());
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
-%1 = OpTypeFunction %2
-%6 = OpTypeFloat 32
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 3
-%5 = OpTypeArray %6 %8
-%9 = OpConstantNull %6
-%10 = OpConstant %6 0.5
-%11 = OpConstant %6 1
-%12 = OpConstantComposite %5 %9 %10 %11
-%15 = OpTypeInt 32 1
-%14 = OpTypePointer Function %15
-%16 = OpConstantNull %15
-%18 = OpTypePointer Function %5
-%19 = OpConstantNull %5
-%21 = OpTypePointer Function %6
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%13 = OpVariable %14 Function %16
-%17 = OpVariable %18 Function %19
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(OpStore %17 %12
-%20 = OpLoad %15 %13
-%22 = OpAccessChain %21 %17 %20
-%23 = OpLoad %6 %22
-OpReturn
-)");
-
-    Validate(b);
-}
-
-TEST_F(BuilderTest, IndexAccessor_Matrix_Dynamic) {
-    // let a : mat2x2<f32>(vec2<f32>(1., 2.), vec2<f32>(3., 4.));
-    // idx : i32
-    // a[idx]
-
-    auto* var = Let("a", ty.mat2x2<f32>(),
-                    Construct(ty.mat2x2<f32>(), Construct(ty.vec2<f32>(), 1_f, 2_f),
-                              Construct(ty.vec2<f32>(), 3_f, 4_f)));
-
-    auto* idx = Var("idx", ty.i32());
-    auto* expr = IndexAccessor("a", idx);
-
-    WrapInFunction(var, idx, expr);
-
-    spirv::Builder& b = SanitizeAndBuild();
-
-    ASSERT_TRUE(b.Build());
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
-%1 = OpTypeFunction %2
-%7 = OpTypeFloat 32
-%6 = OpTypeVector %7 2
-%5 = OpTypeMatrix %6 2
-%8 = OpConstant %7 1
-%9 = OpConstant %7 2
-%10 = OpConstantComposite %6 %8 %9
-%11 = OpConstant %7 3
-%12 = OpConstant %7 4
-%13 = OpConstantComposite %6 %11 %12
-%14 = OpConstantComposite %5 %10 %13
-%17 = OpTypeInt 32 1
-%16 = OpTypePointer Function %17
-%18 = OpConstantNull %17
-%20 = OpTypePointer Function %5
-%21 = OpConstantNull %5
-%23 = OpTypePointer Function %6
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
-              R"(%15 = OpVariable %16 Function %18
-%19 = OpVariable %20 Function %21
-)");
-    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-              R"(OpStore %19 %14
-%22 = OpLoad %17 %15
-%24 = OpAccessChain %23 %19 %22
-%25 = OpLoad %6 %24
+              R"(%23 = OpAccessChain %22 %5 %19 %20 %21 %20 %20
+%25 = OpLoad %12 %23
+%26 = OpVectorShuffle %24 %25 %25 1 0
 OpReturn
 )");
 
diff --git a/src/tint/writer/spirv/builder_assign_test.cc b/src/tint/writer/spirv/builder_assign_test.cc
index 71c958c..526ede8 100644
--- a/src/tint/writer/spirv/builder_assign_test.cc
+++ b/src/tint/writer/spirv/builder_assign_test.cc
@@ -23,7 +23,7 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, Assign_Var) {
-    auto* v = Global("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* assign = Assign("var", 1_f);
 
@@ -51,7 +51,7 @@
 }
 
 TEST_F(BuilderTest, Assign_Var_OutsideFunction_IsError) {
-    auto* v = Global("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* assign = Assign("var", Expr(1_f));
 
@@ -70,7 +70,7 @@
 }
 
 TEST_F(BuilderTest, Assign_Var_ZeroConstructor) {
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* val = vec3<f32>();
     auto* assign = Assign("var", val);
@@ -101,7 +101,7 @@
 TEST_F(BuilderTest, Assign_Var_Complex_ConstructorNestedVector) {
     auto* init = vec3<f32>(vec2<f32>(1_f, 2_f), 3_f);
 
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* assign = Assign("var", init);
 
@@ -134,7 +134,7 @@
 TEST_F(BuilderTest, Assign_Var_Complex_Constructor) {
     auto* init = vec3<f32>(1_f, 2_f, 3_f);
 
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* assign = Assign("var", init);
 
@@ -209,7 +209,7 @@
 }
 
 TEST_F(BuilderTest, Assign_Vector) {
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* val = vec3<f32>(1_f, 1_f, 3_f);
     auto* assign = Assign("var", val);
@@ -243,7 +243,7 @@
 TEST_F(BuilderTest, Assign_Vector_MemberByName) {
     // var.y = 1
 
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* assign = Assign(MemberAccessor("var", "y"), Expr(1_f));
 
@@ -278,7 +278,7 @@
 TEST_F(BuilderTest, Assign_Vector_MemberByIndex) {
     // var[1] = 1
 
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* assign = Assign(IndexAccessor("var", 1_i), Expr(1_f));
 
diff --git a/src/tint/writer/spirv/builder_binary_expression_test.cc b/src/tint/writer/spirv/builder_binary_expression_test.cc
index abaee2e..443e786 100644
--- a/src/tint/writer/spirv/builder_binary_expression_test.cc
+++ b/src/tint/writer/spirv/builder_binary_expression_test.cc
@@ -287,7 +287,7 @@
     EXPECT_EQ(b.GenerateBinaryExpression(expr), 7u) << b.error();
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeBool
 %1 = OpTypeVector %2 3
-%3 = OpConstantFalse %2
+%3 = OpConstantNull %2
 %4 = OpConstantTrue %2
 %5 = OpConstantComposite %1 %3 %4 %3
 %6 = OpConstantComposite %1 %4 %3 %4
@@ -704,8 +704,8 @@
 }
 
 TEST_F(BuilderTest, Binary_LogicalAnd_WithLoads) {
-    auto* a_var = Global("a", ty.bool_(), ast::StorageClass::kPrivate, Expr(true));
-    auto* b_var = Global("b", ty.bool_(), ast::StorageClass::kPrivate, Expr(false));
+    auto* a_var = GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate, Expr(true));
+    auto* b_var = GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate, Expr(false));
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd, Expr("a"), Expr("b"));
 
@@ -857,8 +857,8 @@
 }
 
 TEST_F(BuilderTest, Binary_LogicalOr_WithLoads) {
-    auto* a_var = Global("a", ty.bool_(), ast::StorageClass::kPrivate, Expr(true));
-    auto* b_var = Global("b", ty.bool_(), ast::StorageClass::kPrivate, Expr(false));
+    auto* a_var = GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate, Expr(true));
+    auto* b_var = GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate, Expr(false));
 
     auto* expr = create<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr, Expr("a"), Expr("b"));
 
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index 4f6dca8..c4bf27e 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -41,7 +41,7 @@
 using BuiltinBoolTest = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(BuiltinBoolTest, Call_Bool_Scalar) {
     auto param = GetParam();
-    auto* var = Global("v", ty.bool_(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.bool_(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -67,7 +67,7 @@
 
 TEST_P(BuiltinBoolTest, Call_Bool_Vector) {
     auto param = GetParam();
-    auto* var = Global("v", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<bool>(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -102,7 +102,7 @@
 using BuiltinIntTest = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(BuiltinIntTest, Call_SInt_Scalar) {
     auto param = GetParam();
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -132,7 +132,7 @@
 
 TEST_P(BuiltinIntTest, Call_SInt_Vector) {
     auto param = GetParam();
-    auto* var = Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -163,7 +163,7 @@
 
 TEST_P(BuiltinIntTest, Call_UInt_Scalar) {
     auto param = GetParam();
-    auto* var = Global("v", ty.u32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.u32(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -193,7 +193,7 @@
 
 TEST_P(BuiltinIntTest, Call_UInt_Vector) {
     auto param = GetParam();
-    auto* var = Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -227,7 +227,7 @@
                                          BuiltinData{"reverseBits", "OpBitReverse"}));
 
 TEST_F(BuiltinBuilderTest, Call_Dot_F32) {
-    auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -256,7 +256,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Dot_U32) {
-    auto* var = Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -295,7 +295,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Dot_I32) {
-    auto* var = Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
     auto* expr = Call("dot", "v", "v");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -336,7 +336,7 @@
 using BuiltinDeriveTest = BuiltinBuilderTestWithParam<BuiltinData>;
 TEST_P(BuiltinDeriveTest, Call_Derivative_Scalar) {
     auto param = GetParam();
-    auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.f32(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func =
         Func("func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)});
@@ -364,7 +364,7 @@
 
 TEST_P(BuiltinDeriveTest, Call_Derivative_Vector) {
     auto param = GetParam();
-    auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
     auto* expr = Call(param.name, "v");
     auto* func =
         Func("func", {}, ty.void_(), {CallStmt(expr)}, {Stage(ast::PipelineStage::kFragment)});
@@ -409,9 +409,9 @@
                                          BuiltinData{"fwidthCoarse", "OpFwidthCoarse"}));
 
 TEST_F(BuiltinBuilderTest, Call_Select) {
-    auto* v3 = Global("v3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* v3 = GlobalVar("v3", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
-    auto* bool_v3 = Global("bool_v3", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+    auto* bool_v3 = GlobalVar("bool_v3", ty.vec3<bool>(), ast::StorageClass::kPrivate);
     auto* expr = Call("select", "v3", "v3", "bool_v3");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -451,17 +451,17 @@
     auto* s = ty.sampler(ast::SamplerKind::kComparisonSampler);
     auto* t = ty.depth_texture(ast::TextureDimension::k2d);
 
-    auto* tex = Global("texture", t,
-                       ast::AttributeList{
-                           create<ast::BindingAttribute>(0),
-                           create<ast::GroupAttribute>(0),
-                       });
+    auto* tex = GlobalVar("texture", t,
+                          ast::AttributeList{
+                              create<ast::BindingAttribute>(0u),
+                              create<ast::GroupAttribute>(0u),
+                          });
 
-    auto* sampler = Global("sampler", s,
-                           ast::AttributeList{
-                               create<ast::BindingAttribute>(1),
-                               create<ast::GroupAttribute>(0),
-                           });
+    auto* sampler = GlobalVar("sampler", s,
+                              ast::AttributeList{
+                                  create<ast::BindingAttribute>(1u),
+                                  create<ast::GroupAttribute>(0u),
+                              });
 
     auto* expr1 = Call("textureSampleCompare", "texture", "sampler", vec2<f32>(1_f, 2_f), 2_f);
     auto* expr2 = Call("textureSampleCompare", "texture", "sampler", vec2<f32>(1_f, 2_f), 2_f);
@@ -506,7 +506,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_GLSLMethod_WithLoad) {
-    auto* var = Global("ident", ty.f32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate);
     auto* expr = Call("round", "ident");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -1513,7 +1513,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Determinant) {
-    auto* var = Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
     auto* expr = Call("determinant", "var");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -1548,7 +1548,7 @@
 }
 
 TEST_F(BuiltinBuilderTest, Call_Transpose) {
-    auto* var = Global("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
     auto* expr = Call("transpose", "var");
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -1585,11 +1585,11 @@
 
 TEST_F(BuiltinBuilderTest, Call_ArrayLength) {
     auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
     auto* expr = Call("arrayLength", AddressOf(MemberAccessor("b", "a")));
 
     Func("a_func", {}, ty.void_(),
@@ -1632,11 +1632,11 @@
                                          Member("z", ty.f32()),
                                          Member(4, "a", ty.array<f32>(4)),
                                      });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
     auto* expr = Call("arrayLength", AddressOf(MemberAccessor("b", "a")));
 
     Func("a_func", {}, ty.void_(),
@@ -1676,11 +1676,11 @@
 
 TEST_F(BuiltinBuilderTest, Call_ArrayLength_ViaLets) {
     auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     auto* p = Let("p", nullptr, AddressOf("b"));
     auto* p2 = Let("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
@@ -1736,11 +1736,11 @@
     //   arrayLength(&*p3);
     // }
     auto* s = Structure("my_struct", {Member("a", ty.array<f32>(4))});
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     auto* p = Let("p", nullptr, AddressOf(Deref(AddressOf("b"))));
     auto* p2 = Let("p2", nullptr, AddressOf(Deref(p)));
@@ -1801,11 +1801,11 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          ast::StatementList{
@@ -1865,11 +1865,11 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          ast::StatementList{
@@ -1937,11 +1937,11 @@
     auto* s = Structure("S", {
                                  Member("v", ty.atomic<i32>()),
                              });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          ast::StatementList{
@@ -2010,11 +2010,11 @@
     auto* s = Structure("S", {
                                  Member("v", ty.atomic<u32>()),
                              });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          ast::StatementList{
@@ -2085,11 +2085,11 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          ast::StatementList{
@@ -2161,11 +2161,11 @@
                                  Member("u", ty.atomic<u32>()),
                                  Member("i", ty.atomic<i32>()),
                              });
-    Global("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("b", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     Func("a_func", {}, ty.void_(),
          ast::StatementList{
diff --git a/src/tint/writer/spirv/builder_builtin_texture_test.cc b/src/tint/writer/spirv/builder_builtin_texture_test.cc
index b4abfcf..ea81211 100644
--- a/src/tint/writer/spirv/builder_builtin_texture_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_texture_test.cc
@@ -2683,7 +2683,7 @@
 %26 = OpConstantComposite %14 %23 %24 %25
 %28 = OpTypeInt 32 1
 %27 = OpTypeVector %28 3
-%29 = OpConstant %28 0
+%29 = OpConstantNull %28
 %30 = OpConstant %28 1
 %31 = OpConstant %28 2
 %32 = OpConstantComposite %27 %29 %30 %31
diff --git a/src/tint/writer/spirv/builder_constructor_expression_test.cc b/src/tint/writer/spirv/builder_constructor_expression_test.cc
index d1fba8f..d8cb809 100644
--- a/src/tint/writer/spirv/builder_constructor_expression_test.cc
+++ b/src/tint/writer/spirv/builder_constructor_expression_test.cc
@@ -24,7 +24,7 @@
 
 TEST_F(SpvBuilderConstructorTest, Const) {
     auto* c = Expr(42.2_f);
-    auto* g = Global("g", ty.f32(), c, ast::StorageClass::kPrivate);
+    auto* g = GlobalVar("g", ty.f32(), c, ast::StorageClass::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -439,7 +439,7 @@
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeBool
 %1 = OpTypeVector %2 3
 %3 = OpConstantTrue %2
-%4 = OpConstantFalse %2
+%4 = OpConstantNull %2
 %5 = OpConstantComposite %1 %3 %4 %3
 )");
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
@@ -1073,39 +1073,108 @@
     EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"()");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_F32_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_F32_With_F32) {
     auto* ctor = Construct<f32>(2_f);
     GlobalConst("g", ty.f32(), ctor);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeFloat 32
+%6 = OpConstant %5 2
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %7 %6
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_F32_With_F32) {
+    auto* ctor = Construct<f32>(2_f);
+    GlobalVar("g", ty.f32(), ast::StorageClass::kPrivate, ctor);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
 %2 = OpConstant %1 2
-%4 = OpTypeVoid
-%3 = OpTypeFunction %4
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_U32_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_U32_With_F32) {
     auto* ctor = Construct<u32>(1.5_f);
     GlobalConst("g", ty.u32(), ctor);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeInt 32 0
+%6 = OpConstant %5 1
+%8 = OpTypePointer Function %5
+%9 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %7 %6
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_U32_With_F32) {
+    auto* ctor = Construct<u32>(1.5_f);
+    GlobalVar("g", ty.u32(), ast::StorageClass::kPrivate, ctor);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
 %2 = OpConstant %1 1
-%4 = OpTypeVoid
-%3 = OpTypeFunction %4
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec2_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_F32) {
     auto* cast = vec2<f32>(2_f);
-    auto* g = Global("g", ty.vec2<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec2<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_F32) {
+    auto* cast = vec2<f32>(2_f);
+    auto* g = GlobalVar("g", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -1119,9 +1188,32 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec2_With_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec2_With_Vec2) {
     auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
-    GlobalConst("a", ty.vec2<f32>(), cast);
+    GlobalConst("g", ty.vec2<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec2_With_Vec2) {
+    auto* cast = vec2<f32>(vec2<f32>(2_f, 2_f));
+    GlobalVar("a", ty.vec2<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -1130,16 +1222,41 @@
 %1 = OpTypeVector %2 2
 %3 = OpConstant %2 2
 %4 = OpConstantComposite %1 %3 %3
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
 )");
 
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_Vec3) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec3) {
     auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
-    GlobalConst("a", ty.vec3<f32>(), cast);
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec3) {
+    auto* cast = vec3<f32>(vec3<f32>(2_f, 2_f, 2_f));
+    GlobalVar("a", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -1148,16 +1265,40 @@
 %1 = OpTypeVector %2 3
 %3 = OpConstant %2 2
 %4 = OpConstantComposite %1 %3 %3 %3
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
 )");
 
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec4) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec4) {
     auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
-    GlobalConst("a", ty.vec4<f32>(), cast);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec4) {
+    auto* cast = vec4<f32>(vec4<f32>(2_f, 2_f, 2_f, 2_f));
+    GlobalVar("a", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = SanitizeAndBuild();
     ASSERT_TRUE(b.Build());
@@ -1166,16 +1307,41 @@
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
 %4 = OpConstantComposite %1 %3 %3 %3 %3
-%6 = OpTypeVoid
-%5 = OpTypeFunction %6
+%6 = OpTypePointer Private %1
+%5 = OpVariable %6 Private %4
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
 )");
 
     Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F32) {
     auto* cast = vec3<f32>(2_f);
-    auto* g = Global("g", ty.vec3<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32) {
+    auto* cast = vec3<f32>(2_f);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -1189,55 +1355,110 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_F32_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_F32_Vec2) {
     auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
-    auto* g = Global("g", ty.vec3<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_F32_Vec2) {
+    auto* cast = vec3<f32>(2_f, vec2<f32>(2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 3
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 2
-%5 = OpConstantComposite %4 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %3 %6 %9
+%4 = OpConstantComposite %1 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec3_With_Vec2_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec3_With_Vec2_F32) {
     auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
-    auto* g = Global("g", ty.vec3<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec3<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 3
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec3_With_Vec2_F32) {
+    auto* cast = vec3<f32>(vec2<f32>(2_f, 2_f), 2_f);
+    auto* g = GlobalVar("g", ty.vec3<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 3
-%3 = OpTypeVector %2 2
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %6 %9 %4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32) {
     auto* cast = vec4<f32>(2_f);
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32) {
+    auto* cast = vec4<f32>(2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
@@ -1251,147 +1472,237 @@
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32_F32_Vec2) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_F32_Vec2) {
     auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_F32_Vec2) {
+    auto* cast = vec4<f32>(2_f, 2_f, vec2<f32>(2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 2
-%5 = OpConstantComposite %4 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %3 %3 %6 %9
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32_Vec2_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_Vec2_F32) {
     auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec2_F32) {
+    auto* cast = vec4<f32>(2_f, vec2<f32>(2_f, 2_f), 2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 2
-%5 = OpConstantComposite %4 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %3 %6 %9 %3
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec2_F32_F32) {
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_F32_F32) {
     auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
 
-    b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 11u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 4
-%3 = OpTypeVector %2 2
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantComposite %1 %6 %9 %4 %4
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
 )");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec2_Vec2) {
-    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_F32_F32) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), 2_f, 2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 13u);
-
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 4
-%3 = OpTypeVector %2 2
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%11 = OpSpecConstantOp %2 CompositeExtract %5 8
-%12 = OpSpecConstantOp %2 CompositeExtract %5 10
-%13 = OpSpecConstantComposite %1 %6 %9 %11 %12
-)");
-}
-
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_F32_Vec3) {
-    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
-
-    spirv::Builder& b = Build();
-
-    b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 13u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
 %3 = OpConstant %2 2
-%4 = OpTypeVector %2 3
-%5 = OpConstantComposite %4 %3 %3 %3
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%12 = OpConstant %7 2
-%11 = OpSpecConstantOp %2 CompositeExtract %5 12
-%13 = OpSpecConstantComposite %1 %3 %6 %9 %11
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
-TEST_F(SpvBuilderConstructorTest, Type_ModuleScope_Vec4_With_Vec3_F32) {
-    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
-    auto* g = Global("g", ty.vec4<f32>(), cast, ast::StorageClass::kPrivate);
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec2_Vec2) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec2_Vec2) {
+    auto* cast = vec4<f32>(vec2<f32>(2_f, 2_f), vec2<f32>(2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 13u);
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 4
-%3 = OpTypeVector %2 3
-%4 = OpConstant %2 2
-%5 = OpConstantComposite %3 %4 %4 %4
-%7 = OpTypeInt 32 0
-%8 = OpConstant %7 0
-%6 = OpSpecConstantOp %2 CompositeExtract %5 8
-%10 = OpConstant %7 1
-%9 = OpSpecConstantOp %2 CompositeExtract %5 10
-%12 = OpConstant %7 2
-%11 = OpSpecConstantOp %2 CompositeExtract %5 12
-%13 = OpSpecConstantComposite %1 %6 %9 %11 %4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_F32_Vec3) {
+    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_F32_Vec3) {
+    auto* cast = vec4<f32>(2_f, vec3<f32>(2_f, 2_f, 2_f));
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
+)");
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalConst_Vec4_With_Vec3_F32) {
+    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
+    GlobalConst("g", ty.vec4<f32>(), cast);
+    WrapInFunction(Decl(Var("l", nullptr, Expr("g"))));
+
+    spirv::Builder& b = SanitizeAndBuild();
+    ASSERT_TRUE(b.Build());
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%5 = OpTypeVector %6 4
+%7 = OpConstant %6 2
+%8 = OpConstantComposite %5 %7 %7 %7 %7
+%10 = OpTypePointer Function %5
+%11 = OpConstantNull %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %9 %8
+OpReturn
+)");
+    Validate(b);
+}
+
+TEST_F(SpvBuilderConstructorTest, Type_GlobalVar_Vec4_With_Vec3_F32) {
+    auto* cast = vec4<f32>(vec3<f32>(2_f, 2_f, 2_f), 2_f);
+    auto* g = GlobalVar("g", ty.vec4<f32>(), ast::StorageClass::kPrivate, cast);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_EQ(b.GenerateConstructorExpression(g, cast), 4u);
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 4
+%3 = OpConstant %2 2
+%4 = OpConstantComposite %1 %3 %3 %3 %3
 )");
 }
 
@@ -1990,7 +2301,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_I32) {
-    auto* var = Global("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
 
     auto* cast = vec3<i32>("i");
     WrapInFunction(cast);
@@ -2016,7 +2327,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F32_to_I32) {
-    auto* var = Global("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* cast = vec3<i32>("i");
     WrapInFunction(cast);
@@ -2042,7 +2353,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_U32) {
-    auto* var = Global("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
 
     auto* cast = vec3<u32>("i");
     WrapInFunction(cast);
@@ -2068,7 +2379,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_F32_to_U32) {
-    auto* var = Global("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* cast = vec3<u32>("i");
     WrapInFunction(cast);
@@ -2094,7 +2405,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_I32_to_F32) {
-    auto* var = Global("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<i32>(), ast::StorageClass::kPrivate);
 
     auto* cast = vec3<f32>("i");
     WrapInFunction(cast);
@@ -2120,7 +2431,7 @@
 }
 
 TEST_F(SpvBuilderConstructorTest, Type_Convert_Vectors_U32_to_F32) {
-    auto* var = Global("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("i", ty.vec3<u32>(), ast::StorageClass::kPrivate);
 
     auto* cast = vec3<f32>("i");
     WrapInFunction(cast);
@@ -2208,9 +2519,9 @@
 TEST_F(SpvBuilderConstructorTest, IsConstructorConst_Vector_WithIdent) {
     // vec3<f32>(a, b, c)  -> false
 
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
-    Global("b", ty.f32(), ast::StorageClass::kPrivate);
-    Global("c", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("c", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* t = vec3<f32>("a", "b", "c");
     WrapInFunction(t);
@@ -2280,8 +2591,8 @@
                                          Member("b", ty.vec3<f32>()),
                                      });
 
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
-    Global("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("b", ty.vec3<f32>(), ast::StorageClass::kPrivate);
 
     auto* t = Construct(ty.Of(s), "a", "b");
     WrapInFunction(t);
diff --git a/src/tint/writer/spirv/builder_function_test.cc b/src/tint/writer/spirv/builder_function_test.cc
index b2744ef..91fa71a 100644
--- a/src/tint/writer/spirv/builder_function_test.cc
+++ b/src/tint/writer/spirv/builder_function_test.cc
@@ -61,7 +61,7 @@
 }
 
 TEST_F(BuilderTest, Function_Terminator_ReturnValue) {
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     Func("a_func", {}, ty.f32(), {Return("a")}, {});
 
@@ -198,11 +198,11 @@
 
     auto* s = Structure("Data", {Member("d", ty.f32())});
 
-    Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
diff --git a/src/tint/writer/spirv/builder_function_variable_test.cc b/src/tint/writer/spirv/builder_function_variable_test.cc
index da72233..4a7026a 100644
--- a/src/tint/writer/spirv/builder_function_variable_test.cc
+++ b/src/tint/writer/spirv/builder_function_variable_test.cc
@@ -138,7 +138,7 @@
 )");
 }
 
-TEST_F(BuilderTest, FunctionVar_ConstWithVarInitializer) {
+TEST_F(BuilderTest, FunctionVar_LetWithVarInitializer) {
     // var v : f32 = 1.0;
     // let v2 : f32 = v; // Should generate the load
 
@@ -173,7 +173,38 @@
 )");
 }
 
-TEST_F(BuilderTest, FunctionVar_Const) {
+TEST_F(BuilderTest, FunctionVar_ConstWithVarInitializer) {
+    // const v : f32 = 1.0;
+    // let v2 : f32 = v;
+
+    auto* v = Const("v", ty.f32(), Expr(1_f));
+
+    auto* v2 = Var("v2", ty.f32(), ast::StorageClass::kNone, Expr("v"));
+    WrapInFunction(v, v2);
+
+    spirv::Builder& b = Build();
+
+    b.push_function(Function{});
+    EXPECT_TRUE(b.GenerateFunctionVariable(v)) << b.error();
+    EXPECT_TRUE(b.GenerateFunctionVariable(v2)) << b.error();
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "v2"
+)");
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 1
+%4 = OpTypePointer Function %1
+%5 = OpConstantNull %1
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
+              R"(%3 = OpVariable %4 Function %5
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+              R"(OpStore %3 %2
+)");
+}
+
+TEST_F(BuilderTest, FunctionVar_Let) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
     auto* v = Let("var", ty.vec3<f32>(), init);
@@ -193,5 +224,20 @@
 )");
 }
 
+TEST_F(BuilderTest, FunctionVar_Const) {
+    auto* init = vec3<f32>(1_f, 1_f, 3_f);
+
+    auto* v = Const("var", ty.vec3<f32>(), init);
+
+    WrapInFunction(v);
+
+    spirv::Builder& b = Build();
+
+    EXPECT_TRUE(b.GenerateFunctionVariable(v)) << b.error();
+    ASSERT_FALSE(b.has_error()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), "");  // Not a mistake - 'const' is inlined
+}
+
 }  // namespace
 }  // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/builder_global_variable_test.cc b/src/tint/writer/spirv/builder_global_variable_test.cc
index 9b30bb1..05f6929 100644
--- a/src/tint/writer/spirv/builder_global_variable_test.cc
+++ b/src/tint/writer/spirv/builder_global_variable_test.cc
@@ -25,7 +25,7 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, GlobalVar_WithStorageClass) {
-    auto* v = Global("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -42,7 +42,7 @@
 TEST_F(BuilderTest, GlobalVar_WithConstructor) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
-    auto* v = Global("var", ty.vec3<f32>(), ast::StorageClass::kPrivate, init);
+    auto* v = GlobalVar("var", ty.vec3<f32>(), ast::StorageClass::kPrivate, init);
 
     spirv::Builder& b = Build();
 
@@ -61,35 +61,41 @@
 )");
 }
 
-TEST_F(BuilderTest, GlobalVar_Const) {
-    auto* init = vec3<f32>(1_f, 1_f, 3_f);
+TEST_F(BuilderTest, GlobalConst) {
+    // const c = 42;
+    // var v = c;
 
-    auto* v = GlobalConst("var", ty.vec3<f32>(), init);
+    auto* c = GlobalConst("c", nullptr, Expr(42_a));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-    ASSERT_FALSE(b.has_error()) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
-    EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %5 "var"
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstant %1 42
+%4 = OpTypePointer Private %1
+%3 = OpVariable %4 Private %2
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 3
-%3 = OpConstant %2 1
-%4 = OpConstant %2 3
-%5 = OpConstantComposite %1 %3 %3 %4
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
 )");
+
+    Validate(b);
 }
 
-TEST_F(BuilderTest, GlobalVar_Complex_Constructor) {
-    auto* init = vec3<f32>(1_f, 2_f, 3_f);
+TEST_F(BuilderTest, GlobalConst_Vec_Constructor) {
+    // const c = vec3<f32>(1f, 2f, 3f);
+    // var v = c;
 
-    auto* v = GlobalConst("var", ty.vec3<f32>(), init);
+    auto* c = GlobalConst("c", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-    ASSERT_FALSE(b.has_error()) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 3
@@ -97,18 +103,57 @@
 %4 = OpConstant %2 2
 %5 = OpConstant %2 3
 %6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
 )");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
 }
 
-TEST_F(BuilderTest, GlobalVar_Complex_ConstructorNestedVector) {
-    auto* init = vec3<f32>(vec2<f32>(1_f, 2_f), 3_f);
+TEST_F(BuilderTest, GlobalConst_Vec_AInt_Constructor) {
+    // const c = vec3(1, 2, 3);
+    // var v = c;
 
-    auto* v = GlobalConst("var", ty.vec3<f32>(), init);
+    auto* c = GlobalConst("c", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
 
-    spirv::Builder& b = Build();
+    spirv::Builder& b = SanitizeAndBuild();
 
-    EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-    ASSERT_FALSE(b.has_error()) << b.error();
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstant %2 2
+%5 = OpConstant %2 3
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, GlobalConst_Vec_AFloat_Constructor) {
+    // const c = vec3(1.0, 2.0, 3.0);
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
 
     EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
 %1 = OpTypeVector %2 3
@@ -116,16 +161,54 @@
 %4 = OpConstant %2 2
 %5 = OpConstant %2 3
 %6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
 )");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
+}
+
+TEST_F(BuilderTest, GlobalConst_Nested_Vec_Constructor) {
+    // const c = vec3<f32>(vec2<f32>(1f, 2f), 3f));
+    // var v = c;
+
+    auto* c = GlobalConst("c", nullptr, vec3<f32>(vec2<f32>(1_f, 2_f), 3_f));
+    GlobalVar("v", nullptr, ast::StorageClass::kPrivate, Expr(c));
+
+    spirv::Builder& b = SanitizeAndBuild();
+
+    ASSERT_TRUE(b.Build()) << b.error();
+
+    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstant %2 2
+%5 = OpConstant %2 3
+%6 = OpConstantComposite %1 %3 %4 %5
+%8 = OpTypePointer Private %1
+%7 = OpVariable %8 Private %6
+%10 = OpTypeVoid
+%9 = OpTypeFunction %10
+)");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
+    EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpReturn
+)");
+
+    Validate(b);
 }
 
 TEST_F(BuilderTest, GlobalVar_WithBindingAndGroup) {
     auto* v =
-        Global("var", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
-               ast::AttributeList{
-                   create<ast::BindingAttribute>(2),
-                   create<ast::GroupAttribute>(3),
-               });
+        GlobalVar("var", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
+                  ast::AttributeList{
+                      create<ast::BindingAttribute>(2u),
+                      create<ast::GroupAttribute>(3u),
+                  });
 
     spirv::Builder& b = Build();
 
@@ -365,11 +448,11 @@
                                  Member("b", ty.i32()),
                              });
 
-    Global("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -406,11 +489,11 @@
 
     auto* A = Structure("A", {Member("a", ty.i32())});
     auto* B = Alias("B", ty.Of(A));
-    Global("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -445,11 +528,11 @@
 
     auto* A = Structure("A", {Member("a", ty.i32())});
     auto* B = Alias("B", ty.Of(A));
-    Global("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("b", ty.Of(B), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -483,16 +566,16 @@
     // var<storage, read_write> c : A
 
     auto* A = Structure("A", {Member("a", ty.i32())});
-    Global("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::GroupAttribute>(0),
-               create<ast::BindingAttribute>(0),
-           });
-    Global("c", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::GroupAttribute>(1),
-               create<ast::BindingAttribute>(0),
-           });
+    GlobalVar("b", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::GroupAttribute>(0u),
+                  create<ast::BindingAttribute>(0u),
+              });
+    GlobalVar("c", ty.Of(A), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::GroupAttribute>(1u),
+                  create<ast::BindingAttribute>(0u),
+              });
 
     spirv::Builder& b = SanitizeAndBuild();
 
@@ -529,11 +612,11 @@
     auto* type = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Uint,
                                     ast::Access::kWrite);
 
-    auto* var_a = Global("a", type,
-                         ast::AttributeList{
-                             create<ast::BindingAttribute>(0),
-                             create<ast::GroupAttribute>(0),
-                         });
+    auto* var_a = GlobalVar("a", type,
+                            ast::AttributeList{
+                                create<ast::BindingAttribute>(0u),
+                                create<ast::GroupAttribute>(0u),
+                            });
 
     spirv::Builder& b = Build();
 
@@ -560,19 +643,19 @@
 
     auto* type_a = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Uint,
                                       ast::Access::kReadWrite);
-    auto* var_a = Global("a", type_a, ast::StorageClass::kNone,
-                         ast::AttributeList{
-                             create<ast::BindingAttribute>(0),
-                             create<ast::GroupAttribute>(0),
-                         });
+    auto* var_a = GlobalVar("a", type_a, ast::StorageClass::kNone,
+                            ast::AttributeList{
+                                create<ast::BindingAttribute>(0u),
+                                create<ast::GroupAttribute>(0u),
+                            });
 
     auto* type_b = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Uint,
                                       ast::Access::kWrite);
-    auto* var_b = Global("b", type_b, ast::StorageClass::kNone,
-                         ast::AttributeList{
-                             create<ast::BindingAttribute>(1),
-                             create<ast::GroupAttribute>(0),
-                         });
+    auto* var_b = GlobalVar("b", type_b, ast::StorageClass::kNone,
+                            ast::AttributeList{
+                                create<ast::BindingAttribute>(1u),
+                                create<ast::GroupAttribute>(0u),
+                            });
 
     spirv::Builder& b = Build();
 
@@ -599,16 +682,16 @@
 
 TEST_F(BuilderTest, GlobalVar_WorkgroupWithZeroInit) {
     auto* type_scalar = ty.i32();
-    auto* var_scalar = Global("a", type_scalar, ast::StorageClass::kWorkgroup);
+    auto* var_scalar = GlobalVar("a", type_scalar, ast::StorageClass::kWorkgroup);
 
     auto* type_array = ty.array<f32, 16>();
-    auto* var_array = Global("b", type_array, ast::StorageClass::kWorkgroup);
+    auto* var_array = GlobalVar("b", type_array, ast::StorageClass::kWorkgroup);
 
     auto* type_struct = Structure("C", {
                                            Member("a", ty.i32()),
                                            Member("b", ty.i32()),
                                        });
-    auto* var_struct = Global("c", ty.Of(type_struct), ast::StorageClass::kWorkgroup);
+    auto* var_struct = GlobalVar("c", ty.Of(type_struct), ast::StorageClass::kWorkgroup);
 
     program = std::make_unique<Program>(std::move(*this));
 
diff --git a/src/tint/writer/spirv/builder_ident_expression_test.cc b/src/tint/writer/spirv/builder_ident_expression_test.cc
index e2e9826..0f24146 100644
--- a/src/tint/writer/spirv/builder_ident_expression_test.cc
+++ b/src/tint/writer/spirv/builder_ident_expression_test.cc
@@ -25,9 +25,9 @@
 TEST_F(BuilderTest, IdentifierExpression_GlobalConst) {
     auto* init = vec3<f32>(1_f, 1_f, 3_f);
 
-    auto* v = GlobalConst("var", ty.vec3<f32>(), init);
+    auto* v = GlobalConst("c", ty.vec3<f32>(), init);
 
-    auto* expr = Expr("var");
+    auto* expr = Expr("c");
     WrapInFunction(expr);
 
     spirv::Builder& b = Build();
@@ -35,18 +35,13 @@
     EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
     ASSERT_FALSE(b.has_error()) << b.error();
 
-    EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
-%1 = OpTypeVector %2 3
-%3 = OpConstant %2 1
-%4 = OpConstant %2 3
-%5 = OpConstantComposite %1 %3 %3 %4
-)");
+    EXPECT_EQ(DumpInstructions(b.types()), R"()");
 
-    EXPECT_EQ(b.GenerateIdentifierExpression(expr), 5u);
+    EXPECT_EQ(b.GenerateIdentifierExpression(expr), 0u);
 }
 
 TEST_F(BuilderTest, IdentifierExpression_GlobalVar) {
-    auto* v = Global("var", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* expr = Expr("var");
     WrapInFunction(expr);
@@ -114,8 +109,7 @@
 }
 
 TEST_F(BuilderTest, IdentifierExpression_Load) {
-    auto* var = Global("var", ty.i32(), ast::StorageClass::kPrivate);
-
+    auto* var = GlobalVar("var", ty.i32(), ast::StorageClass::kPrivate);
     auto* expr = Add("var", "var");
     WrapInFunction(expr);
 
@@ -138,15 +132,14 @@
 }
 
 TEST_F(BuilderTest, IdentifierExpression_NoLoadConst) {
-    auto* var = GlobalConst("var", ty.i32(), Expr(2_i));
-
-    auto* expr = Add("var", "var");
-    WrapInFunction(expr);
+    auto* let = Let("let", ty.i32(), Expr(2_i));
+    auto* expr = Add("let", "let");
+    WrapInFunction(let, expr);
 
     spirv::Builder& b = Build();
 
     b.push_function(Function{});
-    ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+    ASSERT_TRUE(b.GenerateFunctionVariable(let)) << b.error();
 
     EXPECT_EQ(b.GenerateBinaryExpression(expr->As<ast::BinaryExpression>()), 3u) << b.error();
     EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
diff --git a/src/tint/writer/spirv/builder_if_test.cc b/src/tint/writer/spirv/builder_if_test.cc
index 8c7c8ee..391be43 100644
--- a/src/tint/writer/spirv/builder_if_test.cc
+++ b/src/tint/writer/spirv/builder_if_test.cc
@@ -68,7 +68,7 @@
     //   v = 2;
     // }
 
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* expr = If(true, body);
     WrapInFunction(expr);
@@ -104,7 +104,7 @@
     //   v = 3i;
     // }
 
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* else_body = Block(Assign("v", 3_i));
 
@@ -146,7 +146,7 @@
     //   v = 3i;
     // }
 
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* else_body = Block(Assign("v", 3_i));
 
@@ -197,7 +197,7 @@
     //   v = 5i;
     // }
 
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* body = Block(Assign("v", 2_i));
     auto* elseif_1_body = Block(Assign("v", 3_i));
     auto* elseif_2_body = Block(Assign("v", 4_i));
@@ -559,7 +559,7 @@
     // if (a) {
     // }
 
-    auto* var = Global("a", ty.bool_(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("a", ty.bool_(), ast::StorageClass::kPrivate);
     auto* fn = Func("f", {}, ty.void_(), {If("a", Block())});
 
     spirv::Builder& b = Build();
diff --git a/src/tint/writer/spirv/builder_loop_test.cc b/src/tint/writer/spirv/builder_loop_test.cc
index b4c2baa..ae9e408 100644
--- a/src/tint/writer/spirv/builder_loop_test.cc
+++ b/src/tint/writer/spirv/builder_loop_test.cc
@@ -54,7 +54,7 @@
     //   break;
     // }
 
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* body = Block(Assign("v", 2_i),  //
                        Break());
 
@@ -96,7 +96,7 @@
     //   }
     // }
 
-    auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* var = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
     auto* body = Block(Assign("v", 2_i),  //
                        Break());
     auto* continuing = Block(Assign("v", 3_i));
diff --git a/src/tint/writer/spirv/builder_switch_test.cc b/src/tint/writer/spirv/builder_switch_test.cc
index 7ddd1bd..3c905b6 100644
--- a/src/tint/writer/spirv/builder_switch_test.cc
+++ b/src/tint/writer/spirv/builder_switch_test.cc
@@ -57,8 +57,8 @@
     //   default: {}
     // }
 
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -114,8 +114,8 @@
     //   default: {}
     // }
 
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = Global("a", ty.u32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.u32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -171,8 +171,8 @@
     //     v = 1i;
     //  }
 
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -221,8 +221,8 @@
     //      v = 3i;
     //  }
 
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -284,8 +284,8 @@
     //      v = 3i;
     //  }
 
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("a_func", {}, ty.void_(),
                       {
@@ -346,8 +346,8 @@
     //   default: {}
     // }
 
-    auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-    auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("v", ty.i32(), ast::StorageClass::kPrivate);
+    auto* a = GlobalVar("a", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* func = Func("a_func", {}, ty.void_(),
                       {
diff --git a/src/tint/writer/spirv/builder_type_test.cc b/src/tint/writer/spirv/builder_type_test.cc
index 8dc048e..b4bff9b 100644
--- a/src/tint/writer/spirv/builder_type_test.cc
+++ b/src/tint/writer/spirv/builder_type_test.cc
@@ -28,11 +28,11 @@
 TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
     auto* ary = ty.array(ty.i32());
     auto* str = Structure("S", {Member("x", ary)});
-    Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -48,11 +48,11 @@
 TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
     auto* ary = ty.array(ty.i32());
     auto* str = Structure("S", {Member("x", ary)});
-    Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -67,7 +67,7 @@
 
 TEST_F(BuilderTest_Type, GenerateArray) {
     auto* ary = ty.array(ty.i32(), 4_u);
-    Global("a", ary, ast::StorageClass::kPrivate);
+    GlobalVar("a", ary, ast::StorageClass::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -84,7 +84,7 @@
 
 TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
     auto* ary = ty.array(ty.i32(), 4_u, 16u);
-    Global("a", ary, ast::StorageClass::kPrivate);
+    GlobalVar("a", ary, ast::StorageClass::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -104,7 +104,7 @@
 
 TEST_F(BuilderTest_Type, ReturnsGeneratedArray) {
     auto* ary = ty.array(ty.i32(), 4_u);
-    Global("a", ary, ast::StorageClass::kPrivate);
+    GlobalVar("a", ary, ast::StorageClass::kPrivate);
 
     spirv::Builder& b = Build();
 
@@ -781,11 +781,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k1d, ast::TexelFormat::kR32Float,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -800,11 +800,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -819,11 +819,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k2dArray, ast::TexelFormat::kR32Float,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -838,11 +838,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k3d, ast::TexelFormat::kR32Float,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -857,11 +857,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -876,11 +876,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Sint,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
@@ -895,11 +895,11 @@
     auto* s = ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Uint,
                                  ast::Access::kWrite);
 
-    Global("test_var", s,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("test_var", s,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     spirv::Builder& b = Build();
 
diff --git a/src/tint/writer/spirv/generator_impl.cc b/src/tint/writer/spirv/generator_impl.cc
index c5e9dad..a435fb8 100644
--- a/src/tint/writer/spirv/generator_impl.cc
+++ b/src/tint/writer/spirv/generator_impl.cc
@@ -46,6 +46,8 @@
 
     {  // Builtin polyfills
         transform::BuiltinPolyfill::Builtins polyfills;
+        polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
+        polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
         polyfills.count_leading_zeros = true;
         polyfills.count_trailing_zeros = true;
         polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
diff --git a/src/tint/writer/text_generator.cc b/src/tint/writer/text_generator.cc
index 5e4e93f..1a1fa5a 100644
--- a/src/tint/writer/text_generator.cc
+++ b/src/tint/writer/text_generator.cc
@@ -84,7 +84,7 @@
                             << "  lines.size(): " << lines.size();
         return;
     }
-    lines.insert(lines.begin() + before, Line{indent, line});
+    lines.insert(lines.begin() + static_cast<int64_t>(before), Line{indent, line});
 }
 
 void TextGenerator::TextBuffer::Append(const TextBuffer& tb) {
@@ -105,7 +105,8 @@
     size_t idx = 0;
     for (auto& line : tb.lines) {
         // TODO(bclayton): inefficent, consider optimizing
-        lines.insert(lines.begin() + before + idx, Line{indent + line.indent, line.content});
+        lines.insert(lines.begin() + static_cast<int64_t>(before + idx),
+                     Line{indent + line.indent, line.content});
         idx++;
     }
 }
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 7df4fdf..26e0a29 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -645,14 +645,6 @@
 
     bool ok = Switch(
         v,  //
-        [&](const ast::Let* ) {
-            out << "let";
-            return true;
-        },
-        [&](const ast::Override* ) {
-            out << "override";
-            return true;
-        },
         [&](const ast::Var* var) {
             out << "var";
             auto sc = var->declared_storage_class;
@@ -669,6 +661,18 @@
             }
             return true;
         },
+        [&](const ast::Let*) {
+            out << "let";
+            return true;
+        },
+        [&](const ast::Override*) {
+            out << "override";
+            return true;
+        },
+        [&](const ast::Const*) {
+            out << "const";
+            return true;
+        },
         [&](Default) {
             TINT_ICE(Writer, diagnostics_) << "unhandled variable type " << v->TypeInfo().name;
             return false;
@@ -710,7 +714,7 @@
             [&](const ast::WorkgroupAttribute* workgroup) {
                 auto values = workgroup->Values();
                 out << "workgroup_size(";
-                for (int i = 0; i < 3; i++) {
+                for (size_t i = 0; i < 3; i++) {
                     if (values[i]) {
                         if (i > 0) {
                             out << ", ";
diff --git a/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc b/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
index 2b4e8b4..d5f7f9f 100644
--- a/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_array_accessor_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, IndexAccessor) {
-    Global("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
     auto* expr = IndexAccessor("ary", 5_i);
     WrapInFunction(expr);
 
@@ -34,7 +34,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, IndexAccessor_OfDref) {
-    Global("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
+    GlobalVar("ary", ty.array<i32, 10>(), ast::StorageClass::kPrivate);
 
     auto* p = Let("p", nullptr, AddressOf("ary"));
     auto* expr = IndexAccessor(Deref("p"), 5_i);
diff --git a/src/tint/writer/wgsl/generator_impl_assign_test.cc b/src/tint/writer/wgsl/generator_impl_assign_test.cc
index 8d5e75e..e390d2f 100644
--- a/src/tint/writer/wgsl/generator_impl_assign_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_assign_test.cc
@@ -20,8 +20,8 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Assign) {
-    auto* lhs = Global("lhs", ty.i32(), ast::StorageClass::kPrivate);
-    auto* rhs = Global("rhs", ty.i32(), ast::StorageClass::kPrivate);
+    auto* lhs = GlobalVar("lhs", ty.i32(), ast::StorageClass::kPrivate);
+    auto* rhs = GlobalVar("rhs", ty.i32(), ast::StorageClass::kPrivate);
     auto* assign = Assign(lhs, rhs);
     WrapInFunction(assign);
 
diff --git a/src/tint/writer/wgsl/generator_impl_binary_test.cc b/src/tint/writer/wgsl/generator_impl_binary_test.cc
index acc4180..682c001 100644
--- a/src/tint/writer/wgsl/generator_impl_binary_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_binary_test.cc
@@ -37,8 +37,8 @@
         }
     };
 
-    Global("left", op_ty(), ast::StorageClass::kPrivate);
-    Global("right", op_ty(), ast::StorageClass::kPrivate);
+    GlobalVar("left", op_ty(), ast::StorageClass::kPrivate);
+    GlobalVar("right", op_ty(), ast::StorageClass::kPrivate);
     auto* left = Expr("left");
     auto* right = Expr("right");
 
diff --git a/src/tint/writer/wgsl/generator_impl_call_test.cc b/src/tint/writer/wgsl/generator_impl_call_test.cc
index 6a7e077..24f7e4b 100644
--- a/src/tint/writer/wgsl/generator_impl_call_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_call_test.cc
@@ -42,8 +42,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.f32(), {Return(1.23_f)});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     WrapInFunction(call);
@@ -62,8 +62,8 @@
              Param(Sym(), ty.f32()),
          },
          ty.void_(), ast::StatementList{}, ast::AttributeList{});
-    Global("param1", ty.f32(), ast::StorageClass::kPrivate);
-    Global("param2", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("param2", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* call = Call("my_func", "param1", "param2");
     auto* stmt = CallStmt(call);
diff --git a/src/tint/writer/wgsl/generator_impl_function_test.cc b/src/tint/writer/wgsl/generator_impl_function_test.cc
index 74f4a6e..0673858 100644
--- a/src/tint/writer/wgsl/generator_impl_function_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_function_test.cc
@@ -160,11 +160,11 @@
 
     auto* s = Structure("Data", {Member("d", ty.f32())});
 
-    Global("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(0),
-               create<ast::GroupAttribute>(0),
-           });
+    GlobalVar("data", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(0u),
+                  create<ast::GroupAttribute>(0u),
+              });
 
     {
         auto* var = Var("v", ty.f32(), ast::StorageClass::kNone, MemberAccessor("data", "d"));
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 8f6d4f0..ef9e11c 100644
--- a/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_global_decl_test.cc
@@ -28,7 +28,7 @@
     auto* func_var = Var("a", ty.f32());
     WrapInFunction(func_var);
 
-    Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -45,7 +45,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_GlobalsInterleaved) {
-    Global("a0", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a0", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* s0 = Structure("S0", {Member("a", ty.i32())});
 
@@ -55,7 +55,7 @@
          },
          {});
 
-    Global("a1", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("a1", ty.f32(), ast::StorageClass::kPrivate);
 
     auto* s1 = Structure("S1", {Member("a", ty.i32())});
 
@@ -101,11 +101,11 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_Global_Sampler) {
-    Global("s", ty.sampler(ast::SamplerKind::kSampler),
-           ast::AttributeList{
-               create<ast::GroupAttribute>(0),
-               create<ast::BindingAttribute>(0),
-           });
+    GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler),
+              ast::AttributeList{
+                  create<ast::GroupAttribute>(0u),
+                  create<ast::BindingAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -117,11 +117,11 @@
 
 TEST_F(WgslGeneratorImplTest, Emit_Global_Texture) {
     auto* st = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
-    Global("t", st,
-           ast::AttributeList{
-               create<ast::GroupAttribute>(0),
-               create<ast::BindingAttribute>(0),
-           });
+    GlobalVar("t", st,
+              ast::AttributeList{
+                  create<ast::GroupAttribute>(0u),
+                  create<ast::BindingAttribute>(0u),
+              });
 
     GeneratorImpl& gen = Build();
 
@@ -131,6 +131,21 @@
     EXPECT_EQ(gen.result(), "  @group(0) @binding(0) var t : texture_1d<f32>;\n");
 }
 
+TEST_F(WgslGeneratorImplTest, Emit_GlobalConst) {
+    GlobalConst("explicit", ty.f32(), Expr(1_f));
+    GlobalConst("inferred", nullptr, Expr(1_f));
+
+    GeneratorImpl& gen = Build();
+
+    gen.increment_indent();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+    EXPECT_EQ(gen.result(), R"(  const explicit : f32 = 1.0f;
+
+  const inferred = 1.0f;
+)");
+}
+
 TEST_F(WgslGeneratorImplTest, Emit_OverridableConstants) {
     Override("a", ty.f32(), nullptr);
     Override("b", ty.f32(), nullptr, {Id(7u)});
diff --git a/src/tint/writer/wgsl/generator_impl_identifier_test.cc b/src/tint/writer/wgsl/generator_impl_identifier_test.cc
index c6bccf6..2e20d4c 100644
--- a/src/tint/writer/wgsl/generator_impl_identifier_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_identifier_test.cc
@@ -20,7 +20,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_Single) {
-    Global("glsl", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("glsl", ty.f32(), ast::StorageClass::kPrivate);
     auto* i = Expr("glsl");
     WrapInFunction(i);
 
diff --git a/src/tint/writer/wgsl/generator_impl_if_test.cc b/src/tint/writer/wgsl/generator_impl_if_test.cc
index 18c0ffd..88f6aba 100644
--- a/src/tint/writer/wgsl/generator_impl_if_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_if_test.cc
@@ -20,7 +20,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_If) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* cond = Expr("cond");
     auto* body = Block(Return());
@@ -39,8 +39,8 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithElseIf) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_cond = Expr("else_cond");
     auto* else_body = Block(Return());
@@ -64,7 +64,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithElse) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_body = Block(Return());
 
@@ -87,8 +87,8 @@
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_IfWithMultiple) {
-    Global("cond", ty.bool_(), ast::StorageClass::kPrivate);
-    Global("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("else_cond", ty.bool_(), ast::StorageClass::kPrivate);
 
     auto* else_cond = Expr("else_cond");
 
diff --git a/src/tint/writer/wgsl/generator_impl_literal_test.cc b/src/tint/writer/wgsl/generator_impl_literal_test.cc
index dd715d4..78d70f6 100644
--- a/src/tint/writer/wgsl/generator_impl_literal_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_literal_test.cc
@@ -25,7 +25,7 @@
 // - 0 sign if sign is 0, 1 otherwise
 // - 'exponent_bits' is placed in the exponent space.
 //   So, the exponent bias must already be included.
-f32 MakeFloat(int sign, int biased_exponent, int mantissa) {
+f32 MakeFloat(uint32_t sign, uint32_t biased_exponent, uint32_t mantissa) {
     const uint32_t sign_bit = sign ? 0x80000000u : 0u;
     // The binary32 exponent is 8 bits, just below the sign.
     const uint32_t exponent_bits = (biased_exponent & 0xffu) << 23;
diff --git a/src/tint/writer/wgsl/generator_impl_loop_test.cc b/src/tint/writer/wgsl/generator_impl_loop_test.cc
index 3dbae60..3570957 100644
--- a/src/tint/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_loop_test.cc
@@ -68,7 +68,7 @@
     // for({ignore(1i); ignore(2i);}; ; ) {
     //   return;
     // }
-    Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
     auto* multi_stmt = Block(Ignore(1_i), Ignore(2_i));
     auto* f = For(multi_stmt, nullptr, nullptr, Block(Return()));
     WrapInFunction(f);
@@ -132,7 +132,7 @@
     //   return;
     // }
 
-    Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
     auto* multi_stmt = Block(Ignore(1_i), Ignore(2_i));
     auto* f = For(nullptr, nullptr, multi_stmt, Block(Return()));
     WrapInFunction(f);
@@ -175,7 +175,7 @@
     // for({ ignore(1i); ignore(2i); }; true; { ignore(3i); ignore(4i); }) {
     //   return;
     // }
-    Global("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
+    GlobalVar("a", ty.atomic<i32>(), ast::StorageClass::kWorkgroup);
     auto* multi_stmt_a = Block(Ignore(1_i), Ignore(2_i));
     auto* multi_stmt_b = Block(Ignore(3_i), Ignore(4_i));
     auto* f = For(multi_stmt_a, Expr(true), multi_stmt_b, Block(Return()));
diff --git a/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc b/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
index d8bd349..34760ba 100644
--- a/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_member_accessor_test.cc
@@ -21,7 +21,7 @@
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_MemberAccessor) {
     auto* s = Structure("Data", {Member("mem", ty.f32())});
-    Global("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
 
     auto* expr = MemberAccessor("str", "mem");
     WrapInFunction(expr);
@@ -35,7 +35,7 @@
 
 TEST_F(WgslGeneratorImplTest, EmitExpression_MemberAccessor_OfDref) {
     auto* s = Structure("Data", {Member("mem", ty.f32())});
-    Global("str", ty.Of(s), ast::StorageClass::kPrivate);
+    GlobalVar("str", ty.Of(s), ast::StorageClass::kPrivate);
 
     auto* p = Let("p", nullptr, AddressOf("str"));
     auto* expr = MemberAccessor(Deref("p"), "mem");
diff --git a/src/tint/writer/wgsl/generator_impl_switch_test.cc b/src/tint/writer/wgsl/generator_impl_switch_test.cc
index 11424f0..141a5d7 100644
--- a/src/tint/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_switch_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_Switch) {
-    Global("cond", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("cond", ty.i32(), ast::StorageClass::kPrivate);
 
     auto* def_body = Block(create<ast::BreakStatement>());
     auto* def = create<ast::CaseStatement>(ast::CaseSelectorList{}, def_body);
diff --git a/src/tint/writer/wgsl/generator_impl_type_test.cc b/src/tint/writer/wgsl/generator_impl_type_test.cc
index 0973ffa..b6ae9c5 100644
--- a/src/tint/writer/wgsl/generator_impl_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_type_test.cc
@@ -420,11 +420,11 @@
     auto param = GetParam();
 
     auto* t = ty.storage_texture(param.dim, param.fmt, param.access);
-    Global("g", t,
-           ast::AttributeList{
-               create<ast::BindingAttribute>(1),
-               create<ast::GroupAttribute>(2),
-           });
+    GlobalVar("g", t,
+              ast::AttributeList{
+                  create<ast::BindingAttribute>(1u),
+                  create<ast::GroupAttribute>(2u),
+              });
 
     GeneratorImpl& gen = Build();
 
diff --git a/src/tint/writer/wgsl/generator_impl_unary_op_test.cc b/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
index 2c46b44..b741dc8 100644
--- a/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_unary_op_test.cc
@@ -20,7 +20,7 @@
 using WgslUnaryOpTest = TestHelper;
 
 TEST_F(WgslUnaryOpTest, AddressOf) {
-    Global("expr", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.f32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("expr"));
     WrapInFunction(op);
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Complement) {
-    Global("expr", ty.u32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.u32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr("expr"));
     WrapInFunction(op);
 
@@ -44,7 +44,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Indirection) {
-    Global("G", ty.f32(), ast::StorageClass::kPrivate);
+    GlobalVar("G", ty.f32(), ast::StorageClass::kPrivate);
     auto* p =
         Let("expr", nullptr, create<ast::UnaryOpExpression>(ast::UnaryOp::kAddressOf, Expr("G")));
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kIndirection, Expr("expr"));
@@ -58,7 +58,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Not) {
-    Global("expr", ty.bool_(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.bool_(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr("expr"));
     WrapInFunction(op);
 
@@ -70,7 +70,7 @@
 }
 
 TEST_F(WgslUnaryOpTest, Negation) {
-    Global("expr", ty.i32(), ast::StorageClass::kPrivate);
+    GlobalVar("expr", ty.i32(), ast::StorageClass::kPrivate);
     auto* op = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr("expr"));
     WrapInFunction(op);
 
diff --git a/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
index a68932c..aeacd30 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_decl_statement_test.cc
@@ -50,5 +50,189 @@
     EXPECT_EQ(gen.result(), "  var a = 123i;\n");
 }
 
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_AInt) {
+    auto* C = Const("C", nullptr, Expr(1_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_AFloat) {
+    auto* C = Const("C", nullptr, Expr(1._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1.0;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_i32) {
+    auto* C = Const("C", nullptr, Expr(1_i));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1i;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_u32) {
+    auto* C = Const("C", nullptr, Expr(1_u));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1u;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_f32) {
+    auto* C = Const("C", nullptr, Expr(1_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = 1.0f;
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = vec3(1, 2, 3);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AFloat) {
+    auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1._a, 2._a, 3._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = vec3(1.0, 2.0, 3.0);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_f32) {
+    auto* C = Const("C", nullptr, vec3<f32>(1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = vec3<f32>(1.0f, 2.0f, 3.0f);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
+    auto* C =
+        Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = mat2x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_f32) {
+    auto* C = Const("C", nullptr, mat2x3<f32>(1_f, 2_f, 3_f, 4_f, 5_f, 6_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = mat2x3<f32>(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_f32) {
+    auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = array<f32, 3u>(1.0f, 2.0f, 3.0f);
+  let l = C;
+}
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_vec2_bool) {
+    auto* C = Const("C", nullptr,
+                    Construct(ty.array(ty.vec2<bool>(), 3_u),  //
+                              vec2<bool>(true, false),         //
+                              vec2<bool>(false, true),         //
+                              vec2<bool>(true, true)));
+    Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+    GeneratorImpl& gen = Build();
+
+    ASSERT_TRUE(gen.Generate()) << gen.error();
+
+    EXPECT_EQ(gen.result(), R"(fn f() {
+  const C = array<vec2<bool>, 3u>(vec2<bool>(true, false), vec2<bool>(false, true), vec2<bool>(true, true));
+  let l = C;
+}
+)");
+}
 }  // namespace
 }  // namespace tint::writer::wgsl
diff --git a/src/tint/writer/wgsl/generator_impl_variable_test.cc b/src/tint/writer/wgsl/generator_impl_variable_test.cc
index 83af7c2..a02aed8 100644
--- a/src/tint/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_variable_test.cc
@@ -22,7 +22,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitVariable) {
-    auto* v = Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -32,7 +32,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_StorageClass) {
-    auto* v = Global("a", ty.f32(), ast::StorageClass::kPrivate);
+    auto* v = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
 
     GeneratorImpl& gen = Build();
 
@@ -43,11 +43,11 @@
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Read) {
     auto* s = Structure("S", {Member("a", ty.i32())});
-    auto* v = Global("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
-                     ast::AttributeList{
-                         create<ast::BindingAttribute>(0),
-                         create<ast::GroupAttribute>(0),
-                     });
+    auto* v = GlobalVar("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+                        ast::AttributeList{
+                            create<ast::BindingAttribute>(0u),
+                            create<ast::GroupAttribute>(0u),
+                        });
 
     GeneratorImpl& gen = Build();
 
@@ -58,11 +58,11 @@
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_Write) {
     auto* s = Structure("S", {Member("a", ty.i32())});
-    auto* v = Global("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
-                     ast::AttributeList{
-                         create<ast::BindingAttribute>(0),
-                         create<ast::GroupAttribute>(0),
-                     });
+    auto* v = GlobalVar("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kWrite,
+                        ast::AttributeList{
+                            create<ast::BindingAttribute>(0u),
+                            create<ast::GroupAttribute>(0u),
+                        });
 
     GeneratorImpl& gen = Build();
 
@@ -73,11 +73,11 @@
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Access_ReadWrite) {
     auto* s = Structure("S", {Member("a", ty.i32())});
-    auto* v = Global("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
-                     ast::AttributeList{
-                         create<ast::BindingAttribute>(0),
-                         create<ast::GroupAttribute>(0),
-                     });
+    auto* v = GlobalVar("a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+                        ast::AttributeList{
+                            create<ast::BindingAttribute>(0u),
+                            create<ast::GroupAttribute>(0u),
+                        });
 
     GeneratorImpl& gen = Build();
 
@@ -87,11 +87,12 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated) {
-    auto* v = Global("a", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
-                     ast::AttributeList{
-                         create<ast::GroupAttribute>(1),
-                         create<ast::BindingAttribute>(2),
-                     });
+    auto* v =
+        GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
+                  ast::AttributeList{
+                      create<ast::GroupAttribute>(1u),
+                      create<ast::BindingAttribute>(2u),
+                  });
 
     GeneratorImpl& gen = Build();
 
@@ -101,7 +102,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Constructor) {
-    auto* v = Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(1_f));
+    auto* v = GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr(1_f));
 
     GeneratorImpl& gen = Build();
 
@@ -110,9 +111,9 @@
     EXPECT_EQ(out.str(), R"(var<private> a : f32 = 1.0f;)");
 }
 
-TEST_F(WgslGeneratorImplTest, EmitVariable_Const) {
+TEST_F(WgslGeneratorImplTest, EmitVariable_Let_Explicit) {
     auto* v = Let("a", ty.f32(), Expr(1_f));
-    WrapInFunction(Decl(v));
+    WrapInFunction(v);
 
     GeneratorImpl& gen = Build();
 
@@ -121,5 +122,38 @@
     EXPECT_EQ(out.str(), R"(let a : f32 = 1.0f;)");
 }
 
+TEST_F(WgslGeneratorImplTest, EmitVariable_Let_Inferred) {
+    auto* v = Let("a", nullptr, Expr(1_f));
+    WrapInFunction(v);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    EXPECT_EQ(out.str(), R"(let a = 1.0f;)");
+}
+
+TEST_F(WgslGeneratorImplTest, EmitVariable_Const_Explicit) {
+    auto* v = Const("a", ty.f32(), Expr(1_f));
+    WrapInFunction(v);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    EXPECT_EQ(out.str(), R"(const a : f32 = 1.0f;)");
+}
+
+TEST_F(WgslGeneratorImplTest, EmitVariable_Const_Inferred) {
+    auto* v = Const("a", nullptr, Expr(1_f));
+    WrapInFunction(v);
+
+    GeneratorImpl& gen = Build();
+
+    std::stringstream out;
+    ASSERT_TRUE(gen.EmitVariable(out, v)) << gen.error();
+    EXPECT_EQ(out.str(), R"(const a = 1.0f;)");
+}
+
 }  // namespace
 }  // namespace tint::writer::wgsl
diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn
deleted file mode 100644
index 0953422..0000000
--- a/test/tint/BUILD.gn
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2022 The Dawn & 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.
-
-group("tint_unittests") {
-  testonly = true
-  public_deps = [ "../../src/tint:tint_unittests" ]
-}